1 // SPDX-License-Identifier: GPL-2.0 2 #include "util/debug.h" 3 #include "util/evlist.h" 4 #include "util/machine.h" 5 #include "util/map.h" 6 #include "util/symbol.h" 7 #include "util/target.h" 8 #include "util/thread_map.h" 9 #include "util/lock-contention.h" 10 #include <linux/zalloc.h> 11 #include <bpf/bpf.h> 12 13 #include "bpf_skel/lock_contention.skel.h" 14 15 static struct lock_contention_bpf *skel; 16 17 /* should be same as bpf_skel/lock_contention.bpf.c */ 18 struct lock_contention_key { 19 s32 stack_id; 20 }; 21 22 struct lock_contention_data { 23 u64 total_time; 24 u64 min_time; 25 u64 max_time; 26 u32 count; 27 u32 flags; 28 }; 29 30 int lock_contention_prepare(struct lock_contention *con) 31 { 32 int i, fd; 33 int ncpus = 1, ntasks = 1; 34 struct evlist *evlist = con->evlist; 35 struct target *target = con->target; 36 37 skel = lock_contention_bpf__open(); 38 if (!skel) { 39 pr_err("Failed to open lock-contention BPF skeleton\n"); 40 return -1; 41 } 42 43 bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries); 44 bpf_map__set_max_entries(skel->maps.lock_stat, con->map_nr_entries); 45 46 if (target__has_cpu(target)) 47 ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus); 48 if (target__has_task(target)) 49 ntasks = perf_thread_map__nr(evlist->core.threads); 50 51 bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); 52 bpf_map__set_max_entries(skel->maps.task_filter, ntasks); 53 54 if (lock_contention_bpf__load(skel) < 0) { 55 pr_err("Failed to load lock-contention BPF skeleton\n"); 56 return -1; 57 } 58 59 if (target__has_cpu(target)) { 60 u32 cpu; 61 u8 val = 1; 62 63 skel->bss->has_cpu = 1; 64 fd = bpf_map__fd(skel->maps.cpu_filter); 65 66 for (i = 0; i < ncpus; i++) { 67 cpu = perf_cpu_map__cpu(evlist->core.user_requested_cpus, i).cpu; 68 bpf_map_update_elem(fd, &cpu, &val, BPF_ANY); 69 } 70 } 71 72 if (target__has_task(target)) { 73 u32 pid; 74 u8 val = 1; 75 76 skel->bss->has_task = 1; 77 fd = bpf_map__fd(skel->maps.task_filter); 78 79 for (i = 0; i < ntasks; i++) { 80 pid = perf_thread_map__pid(evlist->core.threads, i); 81 bpf_map_update_elem(fd, &pid, &val, BPF_ANY); 82 } 83 } 84 85 if (target__none(target) && evlist->workload.pid > 0) { 86 u32 pid = evlist->workload.pid; 87 u8 val = 1; 88 89 skel->bss->has_task = 1; 90 fd = bpf_map__fd(skel->maps.task_filter); 91 bpf_map_update_elem(fd, &pid, &val, BPF_ANY); 92 } 93 94 lock_contention_bpf__attach(skel); 95 return 0; 96 } 97 98 int lock_contention_start(void) 99 { 100 skel->bss->enabled = 1; 101 return 0; 102 } 103 104 int lock_contention_stop(void) 105 { 106 skel->bss->enabled = 0; 107 return 0; 108 } 109 110 int lock_contention_read(struct lock_contention *con) 111 { 112 int fd, stack; 113 s32 prev_key, key; 114 struct lock_contention_data data; 115 struct lock_stat *st; 116 struct machine *machine = con->machine; 117 u64 stack_trace[CONTENTION_STACK_DEPTH]; 118 119 fd = bpf_map__fd(skel->maps.lock_stat); 120 stack = bpf_map__fd(skel->maps.stacks); 121 122 con->lost = skel->bss->lost; 123 124 prev_key = 0; 125 while (!bpf_map_get_next_key(fd, &prev_key, &key)) { 126 struct map *kmap; 127 struct symbol *sym; 128 int idx; 129 130 bpf_map_lookup_elem(fd, &key, &data); 131 st = zalloc(sizeof(*st)); 132 if (st == NULL) 133 return -1; 134 135 st->nr_contended = data.count; 136 st->wait_time_total = data.total_time; 137 st->wait_time_max = data.max_time; 138 st->wait_time_min = data.min_time; 139 140 if (data.count) 141 st->avg_wait_time = data.total_time / data.count; 142 143 st->flags = data.flags; 144 145 bpf_map_lookup_elem(stack, &key, stack_trace); 146 147 /* skip BPF + lock internal functions */ 148 idx = CONTENTION_STACK_SKIP; 149 while (is_lock_function(machine, stack_trace[idx]) && 150 idx < CONTENTION_STACK_DEPTH - 1) 151 idx++; 152 153 st->addr = stack_trace[idx]; 154 sym = machine__find_kernel_symbol(machine, st->addr, &kmap); 155 156 if (sym) { 157 unsigned long offset; 158 int ret = 0; 159 160 offset = kmap->map_ip(kmap, st->addr) - sym->start; 161 162 if (offset) 163 ret = asprintf(&st->name, "%s+%#lx", sym->name, offset); 164 else 165 st->name = strdup(sym->name); 166 167 if (ret < 0 || st->name == NULL) 168 return -1; 169 } else if (asprintf(&st->name, "%#lx", (unsigned long)st->addr) < 0) { 170 free(st); 171 return -1; 172 } 173 174 hlist_add_head(&st->hash_entry, con->result); 175 prev_key = key; 176 } 177 178 return 0; 179 } 180 181 int lock_contention_finish(void) 182 { 183 if (skel) { 184 skel->bss->enabled = 0; 185 lock_contention_bpf__destroy(skel); 186 } 187 188 return 0; 189 } 190