xref: /linux/tools/perf/util/bpf_lock_contention.c (revision 4fd06bd2dcc893d922985cc4e9082477f6d115e6)
1407b36f6SNamhyung Kim // SPDX-License-Identifier: GPL-2.0
2d0c502e4SNamhyung Kim #include "util/cgroup.h"
3407b36f6SNamhyung Kim #include "util/debug.h"
46fda2405SNamhyung Kim #include "util/evlist.h"
5407b36f6SNamhyung Kim #include "util/machine.h"
6407b36f6SNamhyung Kim #include "util/map.h"
7407b36f6SNamhyung Kim #include "util/symbol.h"
86fda2405SNamhyung Kim #include "util/target.h"
9eca949b2SNamhyung Kim #include "util/thread.h"
106fda2405SNamhyung Kim #include "util/thread_map.h"
11407b36f6SNamhyung Kim #include "util/lock-contention.h"
12407b36f6SNamhyung Kim #include <linux/zalloc.h>
13a6eaf966SNamhyung Kim #include <linux/string.h>
14407b36f6SNamhyung Kim #include <bpf/bpf.h>
15407b36f6SNamhyung Kim 
16407b36f6SNamhyung Kim #include "bpf_skel/lock_contention.skel.h"
17fd507d3eSNamhyung Kim #include "bpf_skel/lock_data.h"
18407b36f6SNamhyung Kim 
19407b36f6SNamhyung Kim static struct lock_contention_bpf *skel;
20407b36f6SNamhyung Kim 
21447ec4e5SNamhyung Kim int lock_contention_prepare(struct lock_contention *con)
22407b36f6SNamhyung Kim {
236fda2405SNamhyung Kim 	int i, fd;
24*4fd06bd2SNamhyung Kim 	int ncpus = 1, ntasks = 1, ntypes = 1, naddrs = 1, ncgrps = 1;
25447ec4e5SNamhyung Kim 	struct evlist *evlist = con->evlist;
26447ec4e5SNamhyung Kim 	struct target *target = con->target;
276fda2405SNamhyung Kim 
28407b36f6SNamhyung Kim 	skel = lock_contention_bpf__open();
29407b36f6SNamhyung Kim 	if (!skel) {
30407b36f6SNamhyung Kim 		pr_err("Failed to open lock-contention BPF skeleton\n");
31407b36f6SNamhyung Kim 		return -1;
32407b36f6SNamhyung Kim 	}
33407b36f6SNamhyung Kim 
3496532a83SNamhyung Kim 	bpf_map__set_value_size(skel->maps.stacks, con->max_stack * sizeof(u64));
35ceb13bfcSNamhyung Kim 	bpf_map__set_max_entries(skel->maps.lock_stat, con->map_nr_entries);
36c66a36afSNamhyung Kim 	bpf_map__set_max_entries(skel->maps.tstamp, con->map_nr_entries);
37ceb13bfcSNamhyung Kim 
38ebab2916SNamhyung Kim 	if (con->aggr_mode == LOCK_AGGR_TASK)
39eca949b2SNamhyung Kim 		bpf_map__set_max_entries(skel->maps.task_data, con->map_nr_entries);
40ebab2916SNamhyung Kim 	else
41eca949b2SNamhyung Kim 		bpf_map__set_max_entries(skel->maps.task_data, 1);
42ebab2916SNamhyung Kim 
43ebab2916SNamhyung Kim 	if (con->save_callstack)
44eca949b2SNamhyung Kim 		bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries);
45ebab2916SNamhyung Kim 	else
46ebab2916SNamhyung Kim 		bpf_map__set_max_entries(skel->maps.stacks, 1);
47eca949b2SNamhyung Kim 
486fda2405SNamhyung Kim 	if (target__has_cpu(target))
496fda2405SNamhyung Kim 		ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus);
506fda2405SNamhyung Kim 	if (target__has_task(target))
516fda2405SNamhyung Kim 		ntasks = perf_thread_map__nr(evlist->core.threads);
52529772c4SNamhyung Kim 	if (con->filters->nr_types)
53529772c4SNamhyung Kim 		ntypes = con->filters->nr_types;
54*4fd06bd2SNamhyung Kim 	if (con->filters->nr_cgrps)
55*4fd06bd2SNamhyung Kim 		ncgrps = con->filters->nr_cgrps;
566fda2405SNamhyung Kim 
575e3febe7SNamhyung Kim 	/* resolve lock name filters to addr */
585e3febe7SNamhyung Kim 	if (con->filters->nr_syms) {
595e3febe7SNamhyung Kim 		struct symbol *sym;
605e3febe7SNamhyung Kim 		struct map *kmap;
615e3febe7SNamhyung Kim 		unsigned long *addrs;
625e3febe7SNamhyung Kim 
635e3febe7SNamhyung Kim 		for (i = 0; i < con->filters->nr_syms; i++) {
645e3febe7SNamhyung Kim 			sym = machine__find_kernel_symbol_by_name(con->machine,
655e3febe7SNamhyung Kim 								  con->filters->syms[i],
665e3febe7SNamhyung Kim 								  &kmap);
675e3febe7SNamhyung Kim 			if (sym == NULL) {
685e3febe7SNamhyung Kim 				pr_warning("ignore unknown symbol: %s\n",
695e3febe7SNamhyung Kim 					   con->filters->syms[i]);
705e3febe7SNamhyung Kim 				continue;
715e3febe7SNamhyung Kim 			}
725e3febe7SNamhyung Kim 
735e3febe7SNamhyung Kim 			addrs = realloc(con->filters->addrs,
745e3febe7SNamhyung Kim 					(con->filters->nr_addrs + 1) * sizeof(*addrs));
755e3febe7SNamhyung Kim 			if (addrs == NULL) {
765e3febe7SNamhyung Kim 				pr_warning("memory allocation failure\n");
775e3febe7SNamhyung Kim 				continue;
785e3febe7SNamhyung Kim 			}
795e3febe7SNamhyung Kim 
8078a1f7cdSIan Rogers 			addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, sym->start);
815e3febe7SNamhyung Kim 			con->filters->addrs = addrs;
825e3febe7SNamhyung Kim 		}
835e3febe7SNamhyung Kim 		naddrs = con->filters->nr_addrs;
845e3febe7SNamhyung Kim 	}
855e3febe7SNamhyung Kim 
866fda2405SNamhyung Kim 	bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus);
876fda2405SNamhyung Kim 	bpf_map__set_max_entries(skel->maps.task_filter, ntasks);
88529772c4SNamhyung Kim 	bpf_map__set_max_entries(skel->maps.type_filter, ntypes);
895e3febe7SNamhyung Kim 	bpf_map__set_max_entries(skel->maps.addr_filter, naddrs);
90*4fd06bd2SNamhyung Kim 	bpf_map__set_max_entries(skel->maps.cgroup_filter, ncgrps);
916fda2405SNamhyung Kim 
92407b36f6SNamhyung Kim 	if (lock_contention_bpf__load(skel) < 0) {
93407b36f6SNamhyung Kim 		pr_err("Failed to load lock-contention BPF skeleton\n");
94407b36f6SNamhyung Kim 		return -1;
95407b36f6SNamhyung Kim 	}
96407b36f6SNamhyung Kim 
976fda2405SNamhyung Kim 	if (target__has_cpu(target)) {
986fda2405SNamhyung Kim 		u32 cpu;
996fda2405SNamhyung Kim 		u8 val = 1;
1006fda2405SNamhyung Kim 
1016fda2405SNamhyung Kim 		skel->bss->has_cpu = 1;
1026fda2405SNamhyung Kim 		fd = bpf_map__fd(skel->maps.cpu_filter);
1036fda2405SNamhyung Kim 
1046fda2405SNamhyung Kim 		for (i = 0; i < ncpus; i++) {
1056fda2405SNamhyung Kim 			cpu = perf_cpu_map__cpu(evlist->core.user_requested_cpus, i).cpu;
1066fda2405SNamhyung Kim 			bpf_map_update_elem(fd, &cpu, &val, BPF_ANY);
1076fda2405SNamhyung Kim 		}
1086fda2405SNamhyung Kim 	}
1096fda2405SNamhyung Kim 
1106fda2405SNamhyung Kim 	if (target__has_task(target)) {
1116fda2405SNamhyung Kim 		u32 pid;
1126fda2405SNamhyung Kim 		u8 val = 1;
1136fda2405SNamhyung Kim 
1146fda2405SNamhyung Kim 		skel->bss->has_task = 1;
1156fda2405SNamhyung Kim 		fd = bpf_map__fd(skel->maps.task_filter);
1166fda2405SNamhyung Kim 
1176fda2405SNamhyung Kim 		for (i = 0; i < ntasks; i++) {
1186fda2405SNamhyung Kim 			pid = perf_thread_map__pid(evlist->core.threads, i);
1196fda2405SNamhyung Kim 			bpf_map_update_elem(fd, &pid, &val, BPF_ANY);
1206fda2405SNamhyung Kim 		}
1216fda2405SNamhyung Kim 	}
1226fda2405SNamhyung Kim 
1236fda2405SNamhyung Kim 	if (target__none(target) && evlist->workload.pid > 0) {
1246fda2405SNamhyung Kim 		u32 pid = evlist->workload.pid;
1256fda2405SNamhyung Kim 		u8 val = 1;
1266fda2405SNamhyung Kim 
1276fda2405SNamhyung Kim 		skel->bss->has_task = 1;
1286fda2405SNamhyung Kim 		fd = bpf_map__fd(skel->maps.task_filter);
1296fda2405SNamhyung Kim 		bpf_map_update_elem(fd, &pid, &val, BPF_ANY);
1306fda2405SNamhyung Kim 	}
1316fda2405SNamhyung Kim 
132529772c4SNamhyung Kim 	if (con->filters->nr_types) {
133529772c4SNamhyung Kim 		u8 val = 1;
134529772c4SNamhyung Kim 
135529772c4SNamhyung Kim 		skel->bss->has_type = 1;
136529772c4SNamhyung Kim 		fd = bpf_map__fd(skel->maps.type_filter);
137529772c4SNamhyung Kim 
138529772c4SNamhyung Kim 		for (i = 0; i < con->filters->nr_types; i++)
139529772c4SNamhyung Kim 			bpf_map_update_elem(fd, &con->filters->types[i], &val, BPF_ANY);
140529772c4SNamhyung Kim 	}
141529772c4SNamhyung Kim 
1425e3febe7SNamhyung Kim 	if (con->filters->nr_addrs) {
1435e3febe7SNamhyung Kim 		u8 val = 1;
1445e3febe7SNamhyung Kim 
1455e3febe7SNamhyung Kim 		skel->bss->has_addr = 1;
1465e3febe7SNamhyung Kim 		fd = bpf_map__fd(skel->maps.addr_filter);
1475e3febe7SNamhyung Kim 
1485e3febe7SNamhyung Kim 		for (i = 0; i < con->filters->nr_addrs; i++)
1495e3febe7SNamhyung Kim 			bpf_map_update_elem(fd, &con->filters->addrs[i], &val, BPF_ANY);
1505e3febe7SNamhyung Kim 	}
1515e3febe7SNamhyung Kim 
152*4fd06bd2SNamhyung Kim 	if (con->filters->nr_cgrps) {
153*4fd06bd2SNamhyung Kim 		u8 val = 1;
154*4fd06bd2SNamhyung Kim 
155*4fd06bd2SNamhyung Kim 		skel->bss->has_cgroup = 1;
156*4fd06bd2SNamhyung Kim 		fd = bpf_map__fd(skel->maps.cgroup_filter);
157*4fd06bd2SNamhyung Kim 
158*4fd06bd2SNamhyung Kim 		for (i = 0; i < con->filters->nr_cgrps; i++)
159*4fd06bd2SNamhyung Kim 			bpf_map_update_elem(fd, &con->filters->cgrps[i], &val, BPF_ANY);
160*4fd06bd2SNamhyung Kim 	}
161*4fd06bd2SNamhyung Kim 
162eca949b2SNamhyung Kim 	/* these don't work well if in the rodata section */
163c1da8dd5SNamhyung Kim 	skel->bss->stack_skip = con->stack_skip;
164eca949b2SNamhyung Kim 	skel->bss->aggr_mode = con->aggr_mode;
165ebab2916SNamhyung Kim 	skel->bss->needs_callstack = con->save_callstack;
1663477f079SNamhyung Kim 	skel->bss->lock_owner = con->owner;
167c1da8dd5SNamhyung Kim 
1684d1792d0SNamhyung Kim 	if (con->aggr_mode == LOCK_AGGR_CGROUP) {
1694d1792d0SNamhyung Kim 		if (cgroup_is_v2("perf_event"))
1704d1792d0SNamhyung Kim 			skel->bss->use_cgroup_v2 = 1;
1714d1792d0SNamhyung Kim 
172d0c502e4SNamhyung Kim 		read_all_cgroups(&con->cgroups);
173d0c502e4SNamhyung Kim 	}
174d0c502e4SNamhyung Kim 
175d24c0144SNamhyung Kim 	bpf_program__set_autoload(skel->progs.collect_lock_syms, false);
176d24c0144SNamhyung Kim 
177407b36f6SNamhyung Kim 	lock_contention_bpf__attach(skel);
178407b36f6SNamhyung Kim 	return 0;
179407b36f6SNamhyung Kim }
180407b36f6SNamhyung Kim 
181407b36f6SNamhyung Kim int lock_contention_start(void)
182407b36f6SNamhyung Kim {
183407b36f6SNamhyung Kim 	skel->bss->enabled = 1;
184407b36f6SNamhyung Kim 	return 0;
185407b36f6SNamhyung Kim }
186407b36f6SNamhyung Kim 
187407b36f6SNamhyung Kim int lock_contention_stop(void)
188407b36f6SNamhyung Kim {
189407b36f6SNamhyung Kim 	skel->bss->enabled = 0;
190407b36f6SNamhyung Kim 	return 0;
191407b36f6SNamhyung Kim }
192407b36f6SNamhyung Kim 
193492fef21SNamhyung Kim static const char *lock_contention_get_name(struct lock_contention *con,
194492fef21SNamhyung Kim 					    struct contention_key *key,
1951811e827SNamhyung Kim 					    u64 *stack_trace, u32 flags)
196492fef21SNamhyung Kim {
197492fef21SNamhyung Kim 	int idx = 0;
198492fef21SNamhyung Kim 	u64 addr;
199492fef21SNamhyung Kim 	const char *name = "";
200492fef21SNamhyung Kim 	static char name_buf[KSYM_NAME_LEN];
201492fef21SNamhyung Kim 	struct symbol *sym;
202492fef21SNamhyung Kim 	struct map *kmap;
203492fef21SNamhyung Kim 	struct machine *machine = con->machine;
204492fef21SNamhyung Kim 
205492fef21SNamhyung Kim 	if (con->aggr_mode == LOCK_AGGR_TASK) {
206492fef21SNamhyung Kim 		struct contention_task_data task;
207ebab2916SNamhyung Kim 		int pid = key->pid;
208492fef21SNamhyung Kim 		int task_fd = bpf_map__fd(skel->maps.task_data);
209492fef21SNamhyung Kim 
210492fef21SNamhyung Kim 		/* do not update idle comm which contains CPU number */
211492fef21SNamhyung Kim 		if (pid) {
212492fef21SNamhyung Kim 			struct thread *t = __machine__findnew_thread(machine, /*pid=*/-1, pid);
213492fef21SNamhyung Kim 
214492fef21SNamhyung Kim 			if (t == NULL)
215492fef21SNamhyung Kim 				return name;
216492fef21SNamhyung Kim 			if (!bpf_map_lookup_elem(task_fd, &pid, &task) &&
217492fef21SNamhyung Kim 			    thread__set_comm(t, task.comm, /*timestamp=*/0))
218492fef21SNamhyung Kim 				name = task.comm;
219492fef21SNamhyung Kim 		}
220492fef21SNamhyung Kim 		return name;
221492fef21SNamhyung Kim 	}
222492fef21SNamhyung Kim 
223492fef21SNamhyung Kim 	if (con->aggr_mode == LOCK_AGGR_ADDR) {
224d24c0144SNamhyung Kim 		int lock_fd = bpf_map__fd(skel->maps.lock_syms);
225d24c0144SNamhyung Kim 
226d24c0144SNamhyung Kim 		/* per-process locks set upper bits of the flags */
2271811e827SNamhyung Kim 		if (flags & LCD_F_MMAP_LOCK)
2281811e827SNamhyung Kim 			return "mmap_lock";
2291811e827SNamhyung Kim 		if (flags & LCD_F_SIGHAND_LOCK)
2301811e827SNamhyung Kim 			return "siglock";
231d24c0144SNamhyung Kim 
232d24c0144SNamhyung Kim 		/* global locks with symbols */
2334d1792d0SNamhyung Kim 		sym = machine__find_kernel_symbol(machine, key->lock_addr_or_cgroup, &kmap);
234492fef21SNamhyung Kim 		if (sym)
235d24c0144SNamhyung Kim 			return sym->name;
236d24c0144SNamhyung Kim 
237d24c0144SNamhyung Kim 		/* try semi-global locks collected separately */
2384d1792d0SNamhyung Kim 		if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr_or_cgroup, &flags)) {
239d24c0144SNamhyung Kim 			if (flags == LOCK_CLASS_RQLOCK)
240d24c0144SNamhyung Kim 				return "rq_lock";
241d24c0144SNamhyung Kim 		}
242d24c0144SNamhyung Kim 
243d24c0144SNamhyung Kim 		return "";
244492fef21SNamhyung Kim 	}
245492fef21SNamhyung Kim 
2464d1792d0SNamhyung Kim 	if (con->aggr_mode == LOCK_AGGR_CGROUP) {
2474d1792d0SNamhyung Kim 		u64 cgrp_id = key->lock_addr_or_cgroup;
248d0c502e4SNamhyung Kim 		struct cgroup *cgrp = __cgroup__find(&con->cgroups, cgrp_id);
249d0c502e4SNamhyung Kim 
250d0c502e4SNamhyung Kim 		if (cgrp)
251d0c502e4SNamhyung Kim 			return cgrp->name;
252d0c502e4SNamhyung Kim 
253d0c502e4SNamhyung Kim 		snprintf(name_buf, sizeof(name_buf), "cgroup:%lu", cgrp_id);
254d0c502e4SNamhyung Kim 		return name_buf;
255d0c502e4SNamhyung Kim 	}
256d0c502e4SNamhyung Kim 
257492fef21SNamhyung Kim 	/* LOCK_AGGR_CALLER: skip lock internal functions */
258492fef21SNamhyung Kim 	while (machine__is_lock_function(machine, stack_trace[idx]) &&
259492fef21SNamhyung Kim 	       idx < con->max_stack - 1)
260492fef21SNamhyung Kim 		idx++;
261492fef21SNamhyung Kim 
262492fef21SNamhyung Kim 	addr = stack_trace[idx];
263492fef21SNamhyung Kim 	sym = machine__find_kernel_symbol(machine, addr, &kmap);
264492fef21SNamhyung Kim 
265492fef21SNamhyung Kim 	if (sym) {
266492fef21SNamhyung Kim 		unsigned long offset;
267492fef21SNamhyung Kim 
26878a1f7cdSIan Rogers 		offset = map__map_ip(kmap, addr) - sym->start;
269492fef21SNamhyung Kim 
270492fef21SNamhyung Kim 		if (offset == 0)
271492fef21SNamhyung Kim 			return sym->name;
272492fef21SNamhyung Kim 
273492fef21SNamhyung Kim 		snprintf(name_buf, sizeof(name_buf), "%s+%#lx", sym->name, offset);
274492fef21SNamhyung Kim 	} else {
275492fef21SNamhyung Kim 		snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
276492fef21SNamhyung Kim 	}
277492fef21SNamhyung Kim 
278492fef21SNamhyung Kim 	return name_buf;
279492fef21SNamhyung Kim }
280492fef21SNamhyung Kim 
281447ec4e5SNamhyung Kim int lock_contention_read(struct lock_contention *con)
282407b36f6SNamhyung Kim {
283492fef21SNamhyung Kim 	int fd, stack, err = 0;
2845d8c0f0eSNamhyung Kim 	struct contention_key *prev_key, key = {};
285fd507d3eSNamhyung Kim 	struct contention_data data = {};
2869e9c5f3cSNamhyung Kim 	struct lock_stat *st = NULL;
287447ec4e5SNamhyung Kim 	struct machine *machine = con->machine;
2889e9c5f3cSNamhyung Kim 	u64 *stack_trace;
2899e9c5f3cSNamhyung Kim 	size_t stack_size = con->max_stack * sizeof(*stack_trace);
290407b36f6SNamhyung Kim 
291407b36f6SNamhyung Kim 	fd = bpf_map__fd(skel->maps.lock_stat);
292407b36f6SNamhyung Kim 	stack = bpf_map__fd(skel->maps.stacks);
293407b36f6SNamhyung Kim 
29484c3a2bbSNamhyung Kim 	con->fails.task = skel->bss->task_fail;
29584c3a2bbSNamhyung Kim 	con->fails.stack = skel->bss->stack_fail;
29684c3a2bbSNamhyung Kim 	con->fails.time = skel->bss->time_fail;
297954cdac7SNamhyung Kim 	con->fails.data = skel->bss->data_fail;
2986d499a6bSNamhyung Kim 
2999e9c5f3cSNamhyung Kim 	stack_trace = zalloc(stack_size);
3009e9c5f3cSNamhyung Kim 	if (stack_trace == NULL)
3019e9c5f3cSNamhyung Kim 		return -1;
3029e9c5f3cSNamhyung Kim 
303eca949b2SNamhyung Kim 	if (con->aggr_mode == LOCK_AGGR_TASK) {
304eca949b2SNamhyung Kim 		struct thread *idle = __machine__findnew_thread(machine,
305eca949b2SNamhyung Kim 								/*pid=*/0,
306eca949b2SNamhyung Kim 								/*tid=*/0);
307eca949b2SNamhyung Kim 		thread__set_comm(idle, "swapper", /*timestamp=*/0);
308eca949b2SNamhyung Kim 	}
309eca949b2SNamhyung Kim 
310d24c0144SNamhyung Kim 	if (con->aggr_mode == LOCK_AGGR_ADDR) {
311d24c0144SNamhyung Kim 		DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
312d24c0144SNamhyung Kim 			.flags = BPF_F_TEST_RUN_ON_CPU,
313d24c0144SNamhyung Kim 		);
314d24c0144SNamhyung Kim 		int prog_fd = bpf_program__fd(skel->progs.collect_lock_syms);
315d24c0144SNamhyung Kim 
316d24c0144SNamhyung Kim 		bpf_prog_test_run_opts(prog_fd, &opts);
317d24c0144SNamhyung Kim 	}
318d24c0144SNamhyung Kim 
319688d2e8dSNamhyung Kim 	/* make sure it loads the kernel map */
320ff583dc4SIan Rogers 	map__load(maps__first(machine->kmaps)->map);
321688d2e8dSNamhyung Kim 
322fd507d3eSNamhyung Kim 	prev_key = NULL;
323fd507d3eSNamhyung Kim 	while (!bpf_map_get_next_key(fd, prev_key, &key)) {
324ebab2916SNamhyung Kim 		s64 ls_key;
32516cad1d3SNamhyung Kim 		const char *name;
326407b36f6SNamhyung Kim 
3279e9c5f3cSNamhyung Kim 		/* to handle errors in the loop body */
3289e9c5f3cSNamhyung Kim 		err = -1;
3299e9c5f3cSNamhyung Kim 
330407b36f6SNamhyung Kim 		bpf_map_lookup_elem(fd, &key, &data);
33116cad1d3SNamhyung Kim 		if (con->save_callstack) {
332ebab2916SNamhyung Kim 			bpf_map_lookup_elem(stack, &key.stack_id, stack_trace);
333ebab2916SNamhyung Kim 
334aae7e453SNamhyung Kim 			if (!match_callstack_filter(machine, stack_trace)) {
335aae7e453SNamhyung Kim 				con->nr_filtered += data.count;
336ebab2916SNamhyung Kim 				goto next;
33716cad1d3SNamhyung Kim 			}
338aae7e453SNamhyung Kim 		}
33916cad1d3SNamhyung Kim 
340ebab2916SNamhyung Kim 		switch (con->aggr_mode) {
341ebab2916SNamhyung Kim 		case LOCK_AGGR_CALLER:
342ebab2916SNamhyung Kim 			ls_key = key.stack_id;
343ebab2916SNamhyung Kim 			break;
344ebab2916SNamhyung Kim 		case LOCK_AGGR_TASK:
345ebab2916SNamhyung Kim 			ls_key = key.pid;
346ebab2916SNamhyung Kim 			break;
347ebab2916SNamhyung Kim 		case LOCK_AGGR_ADDR:
3484d1792d0SNamhyung Kim 		case LOCK_AGGR_CGROUP:
3494d1792d0SNamhyung Kim 			ls_key = key.lock_addr_or_cgroup;
350ebab2916SNamhyung Kim 			break;
351ebab2916SNamhyung Kim 		default:
352ebab2916SNamhyung Kim 			goto next;
353ebab2916SNamhyung Kim 		}
354ebab2916SNamhyung Kim 
355ebab2916SNamhyung Kim 		st = lock_stat_find(ls_key);
35616cad1d3SNamhyung Kim 		if (st != NULL) {
35716cad1d3SNamhyung Kim 			st->wait_time_total += data.total_time;
35816cad1d3SNamhyung Kim 			if (st->wait_time_max < data.max_time)
35916cad1d3SNamhyung Kim 				st->wait_time_max = data.max_time;
36016cad1d3SNamhyung Kim 			if (st->wait_time_min > data.min_time)
36116cad1d3SNamhyung Kim 				st->wait_time_min = data.min_time;
36216cad1d3SNamhyung Kim 
36316cad1d3SNamhyung Kim 			st->nr_contended += data.count;
36416cad1d3SNamhyung Kim 			if (st->nr_contended)
36516cad1d3SNamhyung Kim 				st->avg_wait_time = st->wait_time_total / st->nr_contended;
36616cad1d3SNamhyung Kim 			goto next;
36716cad1d3SNamhyung Kim 		}
36816cad1d3SNamhyung Kim 
3691811e827SNamhyung Kim 		name = lock_contention_get_name(con, &key, stack_trace, data.flags);
370ebab2916SNamhyung Kim 		st = lock_stat_findnew(ls_key, name, data.flags);
371407b36f6SNamhyung Kim 		if (st == NULL)
3729e9c5f3cSNamhyung Kim 			break;
373407b36f6SNamhyung Kim 
374407b36f6SNamhyung Kim 		st->nr_contended = data.count;
375407b36f6SNamhyung Kim 		st->wait_time_total = data.total_time;
376407b36f6SNamhyung Kim 		st->wait_time_max = data.max_time;
377407b36f6SNamhyung Kim 		st->wait_time_min = data.min_time;
378407b36f6SNamhyung Kim 
379407b36f6SNamhyung Kim 		if (data.count)
380407b36f6SNamhyung Kim 			st->avg_wait_time = data.total_time / data.count;
381407b36f6SNamhyung Kim 
3820fba2265SNamhyung Kim 		if (con->aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {
3839e9c5f3cSNamhyung Kim 			st->callstack = memdup(stack_trace, stack_size);
3849e9c5f3cSNamhyung Kim 			if (st->callstack == NULL)
3859e9c5f3cSNamhyung Kim 				break;
386a6eaf966SNamhyung Kim 		}
387492fef21SNamhyung Kim 
38816cad1d3SNamhyung Kim next:
389fd507d3eSNamhyung Kim 		prev_key = &key;
3909e9c5f3cSNamhyung Kim 
39116cad1d3SNamhyung Kim 		/* we're fine now, reset the error */
3929e9c5f3cSNamhyung Kim 		err = 0;
393407b36f6SNamhyung Kim 	}
394407b36f6SNamhyung Kim 
3959e9c5f3cSNamhyung Kim 	free(stack_trace);
3969e9c5f3cSNamhyung Kim 
3979e9c5f3cSNamhyung Kim 	return err;
398407b36f6SNamhyung Kim }
399407b36f6SNamhyung Kim 
400d0c502e4SNamhyung Kim int lock_contention_finish(struct lock_contention *con)
401407b36f6SNamhyung Kim {
402407b36f6SNamhyung Kim 	if (skel) {
403407b36f6SNamhyung Kim 		skel->bss->enabled = 0;
404407b36f6SNamhyung Kim 		lock_contention_bpf__destroy(skel);
405407b36f6SNamhyung Kim 	}
406407b36f6SNamhyung Kim 
407d0c502e4SNamhyung Kim 	while (!RB_EMPTY_ROOT(&con->cgroups)) {
408d0c502e4SNamhyung Kim 		struct rb_node *node = rb_first(&con->cgroups);
409d0c502e4SNamhyung Kim 		struct cgroup *cgrp = rb_entry(node, struct cgroup, node);
410d0c502e4SNamhyung Kim 
411d0c502e4SNamhyung Kim 		rb_erase(node, &con->cgroups);
412d0c502e4SNamhyung Kim 		cgroup__put(cgrp);
413d0c502e4SNamhyung Kim 	}
414d0c502e4SNamhyung Kim 
415407b36f6SNamhyung Kim 	return 0;
416407b36f6SNamhyung Kim }
417