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

Configure Feed

Select the types of activity you want to include in your feed.

at v6.13-rc4 771 lines 19 kB view raw
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}