Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf lock: Move common lock contention code to new file

Avoid references from util code to builtin-lock that require python
stubs. Move the functions and related variables to
util/lock-contention.c. Add max_stack_depth parameter to
match_callstack_filter to avoid sharing a global variable.

Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
Cc: Colin Ian King <colin.i.king@gmail.com>
Cc: Dapeng Mi <dapeng1.mi@linux.intel.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ilya Leoshkevich <iii@linux.ibm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Michael Petlan <mpetlan@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Veronika Molnarova <vmolnaro@redhat.com>
Cc: Weilin Wang <weilin.wang@intel.com>
Link: https://lore.kernel.org/r/20241119011644.971342-16-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
1a12ed09 16ecb431

+160 -158
+1 -136
tools/perf/builtin-lock.c
··· 46 46 static struct perf_session *session; 47 47 static struct target target; 48 48 49 - /* based on kernel/lockdep.c */ 50 - #define LOCKHASH_BITS 12 51 - #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) 52 - 53 - static struct hlist_head *lockhash_table; 54 - 55 - #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) 56 - #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) 57 - 58 49 static struct rb_root thread_stats; 59 50 60 51 static bool combine_locks; ··· 58 67 static int max_stack_depth = CONTENTION_STACK_DEPTH; 59 68 static int stack_skip = CONTENTION_STACK_SKIP; 60 69 static int print_nr_entries = INT_MAX / 2; 61 - static LIST_HEAD(callstack_filters); 62 70 static const char *output_name = NULL; 63 71 static FILE *lock_output; 64 - 65 - struct callstack_filter { 66 - struct list_head list; 67 - char name[]; 68 - }; 69 72 70 73 static struct lock_filter filters; 71 74 72 75 static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR; 73 - 74 - static bool needs_callstack(void) 75 - { 76 - return !list_empty(&callstack_filters); 77 - } 78 76 79 77 static struct thread_stat *thread_stat_find(u32 tid) 80 78 { ··· 455 475 456 476 rb_erase(node, &result); 457 477 return container_of(node, struct lock_stat, rb); 458 - } 459 - 460 - struct lock_stat *lock_stat_find(u64 addr) 461 - { 462 - struct hlist_head *entry = lockhashentry(addr); 463 - struct lock_stat *ret; 464 - 465 - hlist_for_each_entry(ret, entry, hash_entry) { 466 - if (ret->addr == addr) 467 - return ret; 468 - } 469 - return NULL; 470 - } 471 - 472 - struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags) 473 - { 474 - struct hlist_head *entry = lockhashentry(addr); 475 - struct lock_stat *ret, *new; 476 - 477 - hlist_for_each_entry(ret, entry, hash_entry) { 478 - if (ret->addr == addr) 479 - return ret; 480 - } 481 - 482 - new = zalloc(sizeof(struct lock_stat)); 483 - if (!new) 484 - goto alloc_failed; 485 - 486 - new->addr = addr; 487 - new->name = strdup(name); 488 - if (!new->name) { 489 - free(new); 490 - goto alloc_failed; 491 - } 492 - 493 - new->flags = flags; 494 - new->wait_time_min = ULLONG_MAX; 495 - 496 - hlist_add_head(&new->hash_entry, entry); 497 - return new; 498 - 499 - alloc_failed: 500 - pr_err("memory allocation failed\n"); 501 - return NULL; 502 - } 503 - 504 - bool match_callstack_filter(struct machine *machine, u64 *callstack) 505 - { 506 - struct map *kmap; 507 - struct symbol *sym; 508 - u64 ip; 509 - const char *arch = perf_env__arch(machine->env); 510 - 511 - if (list_empty(&callstack_filters)) 512 - return true; 513 - 514 - for (int i = 0; i < max_stack_depth; i++) { 515 - struct callstack_filter *filter; 516 - 517 - /* 518 - * In powerpc, the callchain saved by kernel always includes 519 - * first three entries as the NIP (next instruction pointer), 520 - * LR (link register), and the contents of LR save area in the 521 - * second stack frame. In certain scenarios its possible to have 522 - * invalid kernel instruction addresses in either LR or the second 523 - * stack frame's LR. In that case, kernel will store that address as 524 - * zero. 525 - * 526 - * The below check will continue to look into callstack, 527 - * incase first or second callstack index entry has 0 528 - * address for powerpc. 529 - */ 530 - if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") || 531 - (i != 1 && i != 2)))) 532 - break; 533 - 534 - ip = callstack[i]; 535 - sym = machine__find_kernel_symbol(machine, ip, &kmap); 536 - if (sym == NULL) 537 - continue; 538 - 539 - list_for_each_entry(filter, &callstack_filters, list) { 540 - if (strstr(sym->name, filter->name)) 541 - return true; 542 - } 543 - } 544 - return false; 545 478 } 546 479 547 480 struct trace_lock_handler { ··· 1058 1165 if (callstack == NULL) 1059 1166 return -ENOMEM; 1060 1167 1061 - if (!match_callstack_filter(machine, callstack)) { 1168 + if (!match_callstack_filter(machine, callstack, max_stack_depth)) { 1062 1169 free(callstack); 1063 1170 return 0; 1064 1171 } ··· 2336 2443 ret = -1; 2337 2444 break; 2338 2445 } 2339 - } 2340 - 2341 - free(s); 2342 - return ret; 2343 - } 2344 - 2345 - static int parse_call_stack(const struct option *opt __maybe_unused, const char *str, 2346 - int unset __maybe_unused) 2347 - { 2348 - char *s, *tmp, *tok; 2349 - int ret = 0; 2350 - 2351 - s = strdup(str); 2352 - if (s == NULL) 2353 - return -1; 2354 - 2355 - for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { 2356 - struct callstack_filter *entry; 2357 - 2358 - entry = malloc(sizeof(*entry) + strlen(tok) + 1); 2359 - if (entry == NULL) { 2360 - pr_err("Memory allocation failure\n"); 2361 - free(s); 2362 - return -1; 2363 - } 2364 - 2365 - strcpy(entry->name, tok); 2366 - list_add_tail(&entry->list, &callstack_filters); 2367 2446 } 2368 2447 2369 2448 free(s);
+1
tools/perf/util/Build
··· 122 122 perf-util-y += iostat.o 123 123 perf-util-y += stream.o 124 124 perf-util-y += kvm-stat.o 125 + perf-util-y += lock-contention.o 125 126 perf-util-$(CONFIG_AUXTRACE) += auxtrace.o 126 127 perf-util-y += intel-pt-decoder/ 127 128 perf-util-$(CONFIG_AUXTRACE) += intel-pt.o
+1 -1
tools/perf/util/bpf_lock_contention.c
··· 458 458 if (con->save_callstack) { 459 459 bpf_map_lookup_elem(stack, &key.stack_id, stack_trace); 460 460 461 - if (!match_callstack_filter(machine, stack_trace)) { 461 + if (!match_callstack_filter(machine, stack_trace, con->max_stack)) { 462 462 con->nr_filtered += data.count; 463 463 goto next; 464 464 }
+143
tools/perf/util/lock-contention.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include "debug.h" 3 + #include "env.h" 4 + #include "lock-contention.h" 5 + #include "machine.h" 6 + #include "symbol.h" 7 + 8 + #include <limits.h> 9 + #include <string.h> 10 + 11 + #include <linux/hash.h> 12 + #include <linux/zalloc.h> 13 + 14 + #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) 15 + #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) 16 + 17 + struct callstack_filter { 18 + struct list_head list; 19 + char name[]; 20 + }; 21 + 22 + static LIST_HEAD(callstack_filters); 23 + struct hlist_head *lockhash_table; 24 + 25 + int parse_call_stack(const struct option *opt __maybe_unused, const char *str, 26 + int unset __maybe_unused) 27 + { 28 + char *s, *tmp, *tok; 29 + int ret = 0; 30 + 31 + s = strdup(str); 32 + if (s == NULL) 33 + return -1; 34 + 35 + for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { 36 + struct callstack_filter *entry; 37 + 38 + entry = malloc(sizeof(*entry) + strlen(tok) + 1); 39 + if (entry == NULL) { 40 + pr_err("Memory allocation failure\n"); 41 + free(s); 42 + return -1; 43 + } 44 + 45 + strcpy(entry->name, tok); 46 + list_add_tail(&entry->list, &callstack_filters); 47 + } 48 + 49 + free(s); 50 + return ret; 51 + } 52 + 53 + bool needs_callstack(void) 54 + { 55 + return !list_empty(&callstack_filters); 56 + } 57 + 58 + struct lock_stat *lock_stat_find(u64 addr) 59 + { 60 + struct hlist_head *entry = lockhashentry(addr); 61 + struct lock_stat *ret; 62 + 63 + hlist_for_each_entry(ret, entry, hash_entry) { 64 + if (ret->addr == addr) 65 + return ret; 66 + } 67 + return NULL; 68 + } 69 + 70 + struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags) 71 + { 72 + struct hlist_head *entry = lockhashentry(addr); 73 + struct lock_stat *ret, *new; 74 + 75 + hlist_for_each_entry(ret, entry, hash_entry) { 76 + if (ret->addr == addr) 77 + return ret; 78 + } 79 + 80 + new = zalloc(sizeof(struct lock_stat)); 81 + if (!new) 82 + goto alloc_failed; 83 + 84 + new->addr = addr; 85 + new->name = strdup(name); 86 + if (!new->name) { 87 + free(new); 88 + goto alloc_failed; 89 + } 90 + 91 + new->flags = flags; 92 + new->wait_time_min = ULLONG_MAX; 93 + 94 + hlist_add_head(&new->hash_entry, entry); 95 + return new; 96 + 97 + alloc_failed: 98 + pr_err("memory allocation failed\n"); 99 + return NULL; 100 + } 101 + 102 + bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_stack_depth) 103 + { 104 + struct map *kmap; 105 + struct symbol *sym; 106 + u64 ip; 107 + const char *arch = perf_env__arch(machine->env); 108 + 109 + if (list_empty(&callstack_filters)) 110 + return true; 111 + 112 + for (int i = 0; i < max_stack_depth; i++) { 113 + struct callstack_filter *filter; 114 + 115 + /* 116 + * In powerpc, the callchain saved by kernel always includes 117 + * first three entries as the NIP (next instruction pointer), 118 + * LR (link register), and the contents of LR save area in the 119 + * second stack frame. In certain scenarios its possible to have 120 + * invalid kernel instruction addresses in either LR or the second 121 + * stack frame's LR. In that case, kernel will store that address as 122 + * zero. 123 + * 124 + * The below check will continue to look into callstack, 125 + * incase first or second callstack index entry has 0 126 + * address for powerpc. 127 + */ 128 + if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") || 129 + (i != 1 && i != 2)))) 130 + break; 131 + 132 + ip = callstack[i]; 133 + sym = machine__find_kernel_symbol(machine, ip, &kmap); 134 + if (sym == NULL) 135 + continue; 136 + 137 + list_for_each_entry(filter, &callstack_filters, list) { 138 + if (strstr(sym->name, filter->name)) 139 + return true; 140 + } 141 + } 142 + return false; 143 + }
+14 -4
tools/perf/util/lock-contention.h
··· 67 67 */ 68 68 #define MAX_LOCK_DEPTH 48 69 69 70 - struct lock_stat *lock_stat_find(u64 addr); 71 - struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags); 70 + /* based on kernel/lockdep.c */ 71 + #define LOCKHASH_BITS 12 72 + #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) 72 73 73 - bool match_callstack_filter(struct machine *machine, u64 *callstack); 74 + extern struct hlist_head *lockhash_table; 74 75 75 76 /* 76 77 * struct lock_seq_stat: ··· 149 148 bool save_callstack; 150 149 }; 151 150 152 - #ifdef HAVE_BPF_SKEL 151 + struct option; 152 + int parse_call_stack(const struct option *opt, const char *str, int unset); 153 + bool needs_callstack(void); 153 154 155 + struct lock_stat *lock_stat_find(u64 addr); 156 + struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags); 157 + 158 + bool match_callstack_filter(struct machine *machine, u64 *callstack, int max_stack_depth); 159 + 160 + 161 + #ifdef HAVE_BPF_SKEL 154 162 int lock_contention_prepare(struct lock_contention *con); 155 163 int lock_contention_start(void); 156 164 int lock_contention_stop(void);
-17
tools/perf/util/python.c
··· 18 18 #include "mmap.h" 19 19 #include "util/kwork.h" 20 20 #include "util/sample.h" 21 - #include "util/lock-contention.h" 22 21 #include <internal/lib.h> 23 22 #include "../builtin.h" 24 23 ··· 1306 1307 struct kwork_work *perf_kwork_add_work(struct perf_kwork *kwork __maybe_unused, 1307 1308 struct kwork_class *class __maybe_unused, 1308 1309 struct kwork_work *key __maybe_unused) 1309 - { 1310 - return NULL; 1311 - } 1312 - 1313 - bool match_callstack_filter(struct machine *machine __maybe_unused, u64 *callstack __maybe_unused) 1314 - { 1315 - return false; 1316 - } 1317 - 1318 - struct lock_stat *lock_stat_find(u64 addr __maybe_unused) 1319 - { 1320 - return NULL; 1321 - } 1322 - 1323 - struct lock_stat *lock_stat_findnew(u64 addr __maybe_unused, const char *name __maybe_unused, 1324 - int flags __maybe_unused) 1325 1310 { 1326 1311 return NULL; 1327 1312 }