Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1/* SPDX-License-Identifier: GPL-2.0 */
  2/**
  3 * Generic event filter for sampling events in BPF.
  4 *
  5 * The BPF program is fixed and just to read filter expressions in the 'filters'
  6 * map and compare the sample data in order to reject samples that don't match.
  7 * Each filter expression contains a sample flag (term) to compare, an operation
  8 * (==, >=, and so on) and a value.
  9 *
 10 * Note that each entry has an array of filter expressions and it only succeeds
 11 * when all of the expressions are satisfied.  But it supports the logical OR
 12 * using a GROUP operation which is satisfied when any of its member expression
 13 * is evaluated to true.  But it doesn't allow nested GROUP operations for now.
 14 *
 15 * To support non-root users, the filters map can be loaded and pinned in the BPF
 16 * filesystem by root (perf record --setup-filter pin).  Then each user will get
 17 * a new entry in the shared filters map to fill the filter expressions.  And the
 18 * BPF program will find the filter using (task-id, event-id) as a key.
 19 *
 20 * The pinned BPF object (shared for regular users) has:
 21 *
 22 *                  event_hash                   |
 23 *                  |        |                   |
 24 *   event->id ---> |   id   | ---+   idx_hash   |     filters
 25 *                  |        |    |   |      |   |    |       |
 26 *                  |  ....  |    +-> |  idx | --+--> | exprs | --->  perf_bpf_filter_entry[]
 27 *                                |   |      |   |    |       |               .op
 28 *   task id (tgid) --------------+   | .... |   |    |  ...  |               .term (+ part)
 29 *                                               |                            .value
 30 *                                               |
 31 *   ======= (root would skip this part) ========                     (compares it in a loop)
 32 *
 33 * This is used for per-task use cases while system-wide profiling (normally from
 34 * root user) uses a separate copy of the program and the maps for its own so that
 35 * it can proceed even if a lot of non-root users are using the filters at the
 36 * same time.  In this case the filters map has a single entry and no need to use
 37 * the hash maps to get the index (key) of the filters map (IOW it's always 0).
 38 *
 39 * The BPF program returns 1 to accept the sample or 0 to drop it.
 40 * The 'dropped' map is to keep how many samples it dropped by the filter and
 41 * it will be reported as lost samples.
 42 */
 43#include <stdlib.h>
 44#include <fcntl.h>
 45#include <sys/ioctl.h>
 46#include <sys/stat.h>
 47
 48#include <bpf/bpf.h>
 49#include <linux/err.h>
 50#include <linux/list.h>
 51#include <api/fs/fs.h>
 52#include <internal/xyarray.h>
 53#include <perf/threadmap.h>
 54
 55#include "util/debug.h"
 56#include "util/evsel.h"
 57#include "util/target.h"
 58
 59#include "util/bpf-filter.h"
 60#include <util/bpf-filter-flex.h>
 61#include <util/bpf-filter-bison.h>
 62
 63#include "bpf_skel/sample-filter.h"
 64#include "bpf_skel/sample_filter.skel.h"
 65
 66#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
 67
 68#define __PERF_SAMPLE_TYPE(tt, st, opt)	{ tt, #st, opt }
 69#define PERF_SAMPLE_TYPE(_st, opt)	__PERF_SAMPLE_TYPE(PBF_TERM_##_st, PERF_SAMPLE_##_st, opt)
 70
 71/* Index in the pinned 'filters' map.  Should be released after use. */
 72struct pinned_filter_idx {
 73	struct list_head list;
 74	struct evsel *evsel;
 75	u64 event_id;
 76	int hash_idx;
 77};
 78
 79static LIST_HEAD(pinned_filters);
 80
 81static const struct perf_sample_info {
 82	enum perf_bpf_filter_term type;
 83	const char *name;
 84	const char *option;
 85} sample_table[] = {
 86	/* default sample flags */
 87	PERF_SAMPLE_TYPE(IP, NULL),
 88	PERF_SAMPLE_TYPE(TID, NULL),
 89	PERF_SAMPLE_TYPE(PERIOD, NULL),
 90	/* flags mostly set by default, but still have options */
 91	PERF_SAMPLE_TYPE(ID, "--sample-identifier"),
 92	PERF_SAMPLE_TYPE(CPU, "--sample-cpu"),
 93	PERF_SAMPLE_TYPE(TIME, "-T"),
 94	/* optional sample flags */
 95	PERF_SAMPLE_TYPE(ADDR, "-d"),
 96	PERF_SAMPLE_TYPE(DATA_SRC, "-d"),
 97	PERF_SAMPLE_TYPE(PHYS_ADDR, "--phys-data"),
 98	PERF_SAMPLE_TYPE(WEIGHT, "-W"),
 99	PERF_SAMPLE_TYPE(WEIGHT_STRUCT, "-W"),
100	PERF_SAMPLE_TYPE(TRANSACTION, "--transaction"),
101	PERF_SAMPLE_TYPE(CODE_PAGE_SIZE, "--code-page-size"),
102	PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"),
103	PERF_SAMPLE_TYPE(CGROUP, "--all-cgroups"),
104};
105
106static int get_pinned_fd(const char *name);
107
108static const struct perf_sample_info *get_sample_info(enum perf_bpf_filter_term type)
109{
110	size_t i;
111
112	for (i = 0; i < ARRAY_SIZE(sample_table); i++) {
113		if (sample_table[i].type == type)
114			return &sample_table[i];
115	}
116	return NULL;
117}
118
119static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr *expr)
120{
121	const struct perf_sample_info *info;
122
123	if (expr->term >= PBF_TERM_SAMPLE_START && expr->term <= PBF_TERM_SAMPLE_END &&
124	    (evsel->core.attr.sample_type & (1 << (expr->term - PBF_TERM_SAMPLE_START))))
125		return 0;
126
127	if (expr->term == PBF_TERM_UID || expr->term == PBF_TERM_GID) {
128		/* Not dependent on the sample_type as computed from a BPF helper. */
129		return 0;
130	}
131
132	if (expr->op == PBF_OP_GROUP_BEGIN) {
133		struct perf_bpf_filter_expr *group;
134
135		list_for_each_entry(group, &expr->groups, list) {
136			if (check_sample_flags(evsel, group) < 0)
137				return -1;
138		}
139		return 0;
140	}
141
142	info = get_sample_info(expr->term);
143	if (info == NULL) {
144		pr_err("Error: %s event does not have sample flags %d\n",
145		       evsel__name(evsel), expr->term);
146		return -1;
147	}
148
149	pr_err("Error: %s event does not have %s\n", evsel__name(evsel), info->name);
150	if (info->option)
151		pr_err(" Hint: please add %s option to perf record\n", info->option);
152	return -1;
153}
154
155static int get_filter_entries(struct evsel *evsel, struct perf_bpf_filter_entry *entry)
156{
157	int i = 0;
158	struct perf_bpf_filter_expr *expr;
159
160	list_for_each_entry(expr, &evsel->bpf_filters, list) {
161		if (check_sample_flags(evsel, expr) < 0)
162			return -EINVAL;
163
164		if (i == MAX_FILTERS)
165			return -E2BIG;
166
167		entry[i].op = expr->op;
168		entry[i].part = expr->part;
169		entry[i].term = expr->term;
170		entry[i].value = expr->val;
171		i++;
172
173		if (expr->op == PBF_OP_GROUP_BEGIN) {
174			struct perf_bpf_filter_expr *group;
175
176			list_for_each_entry(group, &expr->groups, list) {
177				if (i == MAX_FILTERS)
178					return -E2BIG;
179
180				entry[i].op = group->op;
181				entry[i].part = group->part;
182				entry[i].term = group->term;
183				entry[i].value = group->val;
184				i++;
185			}
186
187			if (i == MAX_FILTERS)
188				return -E2BIG;
189
190			entry[i].op = PBF_OP_GROUP_END;
191			i++;
192		}
193	}
194
195	if (i < MAX_FILTERS) {
196		/* to terminate the loop early */
197		entry[i].op = PBF_OP_DONE;
198		i++;
199	}
200	return 0;
201}
202
203static int convert_to_tgid(int tid)
204{
205	char path[128];
206	char *buf, *p, *q;
207	int tgid;
208	size_t len;
209
210	scnprintf(path, sizeof(path), "%d/status", tid);
211	if (procfs__read_str(path, &buf, &len) < 0)
212		return -1;
213
214	p = strstr(buf, "Tgid:");
215	if (p == NULL) {
216		free(buf);
217		return -1;
218	}
219
220	tgid = strtol(p + 6, &q, 0);
221	free(buf);
222	if (*q != '\n')
223		return -1;
224
225	return tgid;
226}
227
228/*
229 * The event might be closed already so we cannot get the list of ids using FD
230 * like in create_event_hash() below, let's iterate the event_hash map and
231 * delete all entries that have the event id as a key.
232 */
233static void destroy_event_hash(u64 event_id)
234{
235	int fd;
236	u64 key, *prev_key = NULL;
237	int num = 0, alloced = 32;
238	u64 *ids = calloc(alloced, sizeof(*ids));
239
240	if (ids == NULL)
241		return;
242
243	fd = get_pinned_fd("event_hash");
244	if (fd < 0) {
245		pr_debug("cannot get fd for 'event_hash' map\n");
246		free(ids);
247		return;
248	}
249
250	/* Iterate the whole map to collect keys for the event id. */
251	while (!bpf_map_get_next_key(fd, prev_key, &key)) {
252		u64 id;
253
254		if (bpf_map_lookup_elem(fd, &key, &id) == 0 && id == event_id) {
255			if (num == alloced) {
256				void *tmp;
257
258				alloced *= 2;
259				tmp = realloc(ids, alloced * sizeof(*ids));
260				if (tmp == NULL)
261					break;
262
263				ids = tmp;
264			}
265			ids[num++] = key;
266		}
267
268		prev_key = &key;
269	}
270
271	for (int i = 0; i < num; i++)
272		bpf_map_delete_elem(fd, &ids[i]);
273
274	free(ids);
275	close(fd);
276}
277
278/*
279 * Return a representative id if ok, or 0 for failures.
280 *
281 * The perf_event->id is good for this, but an evsel would have multiple
282 * instances for CPUs and tasks.  So pick up the first id and setup a hash
283 * from id of each instance to the representative id (the first one).
284 */
285static u64 create_event_hash(struct evsel *evsel)
286{
287	int x, y, fd;
288	u64 the_id = 0, id;
289
290	fd = get_pinned_fd("event_hash");
291	if (fd < 0) {
292		pr_err("cannot get fd for 'event_hash' map\n");
293		return 0;
294	}
295
296	for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
297		for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
298			int ret = ioctl(FD(evsel, x, y), PERF_EVENT_IOC_ID, &id);
299
300			if (ret < 0) {
301				pr_err("Failed to get the event id\n");
302				if (the_id)
303					destroy_event_hash(the_id);
304				return 0;
305			}
306
307			if (the_id == 0)
308				the_id = id;
309
310			bpf_map_update_elem(fd, &id, &the_id, BPF_ANY);
311		}
312	}
313
314	close(fd);
315	return the_id;
316}
317
318static void destroy_idx_hash(struct pinned_filter_idx *pfi)
319{
320	int fd, nr;
321	struct perf_thread_map *threads;
322
323	fd = get_pinned_fd("filters");
324	bpf_map_delete_elem(fd, &pfi->hash_idx);
325	close(fd);
326
327	if (pfi->event_id)
328		destroy_event_hash(pfi->event_id);
329
330	threads = perf_evsel__threads(&pfi->evsel->core);
331	if (threads == NULL)
332		return;
333
334	fd = get_pinned_fd("idx_hash");
335	nr = perf_thread_map__nr(threads);
336	for (int i = 0; i < nr; i++) {
337		/* The target task might be dead already, just try the pid */
338		struct idx_hash_key key = {
339			.evt_id = pfi->event_id,
340			.tgid = perf_thread_map__pid(threads, i),
341		};
342
343		bpf_map_delete_elem(fd, &key);
344	}
345	close(fd);
346}
347
348/* Maintain a hashmap from (tgid, event-id) to filter index */
349static int create_idx_hash(struct evsel *evsel, struct perf_bpf_filter_entry *entry)
350{
351	int filter_idx;
352	int fd, nr, last;
353	u64 event_id = 0;
354	struct pinned_filter_idx *pfi = NULL;
355	struct perf_thread_map *threads;
356
357	fd = get_pinned_fd("filters");
358	if (fd < 0) {
359		pr_err("cannot get fd for 'filters' map\n");
360		return fd;
361	}
362
363	/* Find the first available entry in the filters map */
364	for (filter_idx = 0; filter_idx < MAX_FILTERS; filter_idx++) {
365		if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) == 0)
366			break;
367	}
368	close(fd);
369
370	if (filter_idx == MAX_FILTERS) {
371		pr_err("Too many users for the filter map\n");
372		return -EBUSY;
373	}
374
375	pfi = zalloc(sizeof(*pfi));
376	if (pfi == NULL) {
377		pr_err("Cannot save pinned filter index\n");
378		return -ENOMEM;
379	}
380
381	pfi->evsel = evsel;
382	pfi->hash_idx = filter_idx;
383
384	event_id = create_event_hash(evsel);
385	if (event_id == 0) {
386		pr_err("Cannot update the event hash\n");
387		goto err;
388	}
389
390	pfi->event_id = event_id;
391
392	threads = perf_evsel__threads(&evsel->core);
393	if (threads == NULL) {
394		pr_err("Cannot get the thread list of the event\n");
395		goto err;
396	}
397
398	/* save the index to a hash map */
399	fd = get_pinned_fd("idx_hash");
400	if (fd < 0) {
401		pr_err("cannot get fd for 'idx_hash' map\n");
402		goto err;
403	}
404
405	last = -1;
406	nr = perf_thread_map__nr(threads);
407	for (int i = 0; i < nr; i++) {
408		int pid = perf_thread_map__pid(threads, i);
409		int tgid;
410		struct idx_hash_key key = {
411			.evt_id = event_id,
412		};
413
414		/* it actually needs tgid, let's get tgid from /proc. */
415		tgid = convert_to_tgid(pid);
416		if (tgid < 0) {
417			/* the thread may be dead, ignore. */
418			continue;
419		}
420
421		if (tgid == last)
422			continue;
423		last = tgid;
424		key.tgid = tgid;
425
426		if (bpf_map_update_elem(fd, &key, &filter_idx, BPF_ANY) < 0) {
427			pr_err("Failed to update the idx_hash\n");
428			close(fd);
429			goto err;
430		}
431		pr_debug("bpf-filter: idx_hash (task=%d,%s) -> %d\n",
432			 tgid, evsel__name(evsel), filter_idx);
433	}
434
435	list_add(&pfi->list, &pinned_filters);
436	close(fd);
437	return filter_idx;
438
439err:
440	destroy_idx_hash(pfi);
441	free(pfi);
442	return -1;
443}
444
445int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target)
446{
447	int i, x, y, fd, ret;
448	struct sample_filter_bpf *skel = NULL;
449	struct bpf_program *prog;
450	struct bpf_link *link;
451	struct perf_bpf_filter_entry *entry;
452	bool needs_idx_hash = !target__has_cpu(target) && !target->uid_str;
453
454	entry = calloc(MAX_FILTERS, sizeof(*entry));
455	if (entry == NULL)
456		return -1;
457
458	ret = get_filter_entries(evsel, entry);
459	if (ret < 0) {
460		pr_err("Failed to process filter entries\n");
461		goto err;
462	}
463
464	if (needs_idx_hash && geteuid() != 0) {
465		int zero = 0;
466
467		/* The filters map is shared among other processes */
468		ret = create_idx_hash(evsel, entry);
469		if (ret < 0)
470			goto err;
471
472		fd = get_pinned_fd("dropped");
473		if (fd < 0) {
474			ret = fd;
475			goto err;
476		}
477
478		/* Reset the lost count */
479		bpf_map_update_elem(fd, &ret, &zero, BPF_ANY);
480		close(fd);
481
482		fd = get_pinned_fd("perf_sample_filter");
483		if (fd < 0) {
484			ret = fd;
485			goto err;
486		}
487
488		for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
489			for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
490				ret = ioctl(FD(evsel, x, y), PERF_EVENT_IOC_SET_BPF, fd);
491				if (ret < 0) {
492					pr_err("Failed to attach perf sample-filter\n");
493					close(fd);
494					goto err;
495				}
496			}
497		}
498
499		close(fd);
500		free(entry);
501		return 0;
502	}
503
504	skel = sample_filter_bpf__open_and_load();
505	if (!skel) {
506		ret = -errno;
507		pr_err("Failed to load perf sample-filter BPF skeleton\n");
508		goto err;
509	}
510
511	i = 0;
512	fd = bpf_map__fd(skel->maps.filters);
513
514	/* The filters map has only one entry in this case */
515	if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) {
516		ret = -errno;
517		pr_err("Failed to update the filter map\n");
518		goto err;
519	}
520
521	prog = skel->progs.perf_sample_filter;
522	for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
523		for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
524			link = bpf_program__attach_perf_event(prog, FD(evsel, x, y));
525			if (IS_ERR(link)) {
526				pr_err("Failed to attach perf sample-filter program\n");
527				ret = PTR_ERR(link);
528				goto err;
529			}
530		}
531	}
532	free(entry);
533	evsel->bpf_skel = skel;
534	return 0;
535
536err:
537	free(entry);
538	if (!list_empty(&pinned_filters)) {
539		struct pinned_filter_idx *pfi, *tmp;
540
541		list_for_each_entry_safe(pfi, tmp, &pinned_filters, list) {
542			destroy_idx_hash(pfi);
543			list_del(&pfi->list);
544			free(pfi);
545		}
546	}
547	sample_filter_bpf__destroy(skel);
548	return ret;
549}
550
551int perf_bpf_filter__destroy(struct evsel *evsel)
552{
553	struct perf_bpf_filter_expr *expr, *tmp;
554	struct pinned_filter_idx *pfi, *pos;
555
556	list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) {
557		list_del(&expr->list);
558		free(expr);
559	}
560	sample_filter_bpf__destroy(evsel->bpf_skel);
561
562	list_for_each_entry_safe(pfi, pos, &pinned_filters, list) {
563		destroy_idx_hash(pfi);
564		list_del(&pfi->list);
565		free(pfi);
566	}
567	return 0;
568}
569
570u64 perf_bpf_filter__lost_count(struct evsel *evsel)
571{
572	int count = 0;
573
574	if (list_empty(&evsel->bpf_filters))
575		return 0;
576
577	if (!list_empty(&pinned_filters)) {
578		int fd = get_pinned_fd("dropped");
579		struct pinned_filter_idx *pfi;
580
581		if (fd < 0)
582			return 0;
583
584		list_for_each_entry(pfi, &pinned_filters, list) {
585			if (pfi->evsel != evsel)
586				continue;
587
588			bpf_map_lookup_elem(fd, &pfi->hash_idx, &count);
589			break;
590		}
591		close(fd);
592	} else if (evsel->bpf_skel) {
593		struct sample_filter_bpf *skel = evsel->bpf_skel;
594		int fd = bpf_map__fd(skel->maps.dropped);
595		int idx = 0;
596
597		bpf_map_lookup_elem(fd, &idx, &count);
598	}
599
600	return count;
601}
602
603struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filter_term term,
604						       int part,
605						       enum perf_bpf_filter_op op,
606						       unsigned long val)
607{
608	struct perf_bpf_filter_expr *expr;
609
610	expr = malloc(sizeof(*expr));
611	if (expr != NULL) {
612		expr->term = term;
613		expr->part = part;
614		expr->op = op;
615		expr->val = val;
616		INIT_LIST_HEAD(&expr->groups);
617	}
618	return expr;
619}
620
621int perf_bpf_filter__parse(struct list_head *expr_head, const char *str)
622{
623	YY_BUFFER_STATE buffer;
624	int ret;
625
626	buffer = perf_bpf_filter__scan_string(str);
627
628	ret = perf_bpf_filter_parse(expr_head);
629
630	perf_bpf_filter__flush_buffer(buffer);
631	perf_bpf_filter__delete_buffer(buffer);
632	perf_bpf_filter_lex_destroy();
633
634	return ret;
635}
636
637int perf_bpf_filter__pin(void)
638{
639	struct sample_filter_bpf *skel;
640	char *path = NULL;
641	int dir_fd, ret = -1;
642
643	skel = sample_filter_bpf__open();
644	if (!skel) {
645		ret = -errno;
646		pr_err("Failed to open perf sample-filter BPF skeleton\n");
647		goto err;
648	}
649
650	/* pinned program will use pid-hash */
651	bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS);
652	bpf_map__set_max_entries(skel->maps.event_hash, MAX_EVT_HASH);
653	bpf_map__set_max_entries(skel->maps.idx_hash, MAX_IDX_HASH);
654	bpf_map__set_max_entries(skel->maps.dropped, MAX_FILTERS);
655	skel->rodata->use_idx_hash = 1;
656
657	if (sample_filter_bpf__load(skel) < 0) {
658		ret = -errno;
659		pr_err("Failed to load perf sample-filter BPF skeleton\n");
660		goto err;
661	}
662
663	if (asprintf(&path, "%s/fs/bpf/%s", sysfs__mountpoint(),
664		     PERF_BPF_FILTER_PIN_PATH) < 0) {
665		ret = -errno;
666		pr_err("Failed to allocate pathname in the BPF-fs\n");
667		goto err;
668	}
669
670	ret = bpf_object__pin(skel->obj, path);
671	if (ret < 0) {
672		pr_err("Failed to pin BPF filter objects\n");
673		goto err;
674	}
675
676	/* setup access permissions for the pinned objects */
677	dir_fd = open(path, O_PATH);
678	if (dir_fd < 0) {
679		bpf_object__unpin(skel->obj, path);
680		ret = dir_fd;
681		goto err;
682	}
683
684	/* BPF-fs root has the sticky bit */
685	if (fchmodat(dir_fd, "..", 01755, 0) < 0) {
686		pr_debug("chmod for BPF-fs failed\n");
687		ret = -errno;
688		goto err_close;
689	}
690
691	/* perf_filter directory */
692	if (fchmodat(dir_fd, ".", 0755, 0) < 0) {
693		pr_debug("chmod for perf_filter directory failed?\n");
694		ret = -errno;
695		goto err_close;
696	}
697
698	/* programs need write permission for some reason */
699	if (fchmodat(dir_fd, "perf_sample_filter", 0777, 0) < 0) {
700		pr_debug("chmod for perf_sample_filter failed\n");
701		ret = -errno;
702	}
703	/* maps */
704	if (fchmodat(dir_fd, "filters", 0666, 0) < 0) {
705		pr_debug("chmod for filters failed\n");
706		ret = -errno;
707	}
708	if (fchmodat(dir_fd, "event_hash", 0666, 0) < 0) {
709		pr_debug("chmod for event_hash failed\n");
710		ret = -errno;
711	}
712	if (fchmodat(dir_fd, "idx_hash", 0666, 0) < 0) {
713		pr_debug("chmod for idx_hash failed\n");
714		ret = -errno;
715	}
716	if (fchmodat(dir_fd, "dropped", 0666, 0) < 0) {
717		pr_debug("chmod for dropped failed\n");
718		ret = -errno;
719	}
720
721err_close:
722	close(dir_fd);
723
724err:
725	free(path);
726	sample_filter_bpf__destroy(skel);
727	return ret;
728}
729
730int perf_bpf_filter__unpin(void)
731{
732	struct sample_filter_bpf *skel;
733	char *path = NULL;
734	int ret = -1;
735
736	skel = sample_filter_bpf__open_and_load();
737	if (!skel) {
738		ret = -errno;
739		pr_err("Failed to open perf sample-filter BPF skeleton\n");
740		goto err;
741	}
742
743	if (asprintf(&path, "%s/fs/bpf/%s", sysfs__mountpoint(),
744		     PERF_BPF_FILTER_PIN_PATH) < 0) {
745		ret = -errno;
746		pr_err("Failed to allocate pathname in the BPF-fs\n");
747		goto err;
748	}
749
750	ret = bpf_object__unpin(skel->obj, path);
751
752err:
753	free(path);
754	sample_filter_bpf__destroy(skel);
755	return ret;
756}
757
758static int get_pinned_fd(const char *name)
759{
760	char *path = NULL;
761	int fd;
762
763	if (asprintf(&path, "%s/fs/bpf/%s/%s", sysfs__mountpoint(),
764		     PERF_BPF_FILTER_PIN_PATH, name) < 0)
765		return -1;
766
767	fd = bpf_obj_get(path);
768
769	free(path);
770	return fd;
771}