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

perf bpf-filter: Support pin/unpin BPF object

And use the pinned objects for unprivileged users to profile their own
tasks. The BPF objects need to be pinned in the BPF-fs by root first
and it'll be handled in the later patch.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: KP Singh <kpsingh@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Song Liu <song@kernel.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: bpf@vger.kernel.org
Link: https://lore.kernel.org/r/20240703223035.2024586-5-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
0715f65e eb1693b1

+211 -36
+198 -36
tools/perf/util/bpf-filter.c
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 #include <stdlib.h> 3 + #include <fcntl.h> 4 + #include <sys/ioctl.h> 5 + #include <sys/stat.h> 3 6 4 7 #include <bpf/bpf.h> 5 8 #include <linux/err.h> ··· 25 22 26 23 #define __PERF_SAMPLE_TYPE(tt, st, opt) { tt, #st, opt } 27 24 #define PERF_SAMPLE_TYPE(_st, opt) __PERF_SAMPLE_TYPE(PBF_TERM_##_st, PERF_SAMPLE_##_st, opt) 25 + 26 + /* Index in the pinned 'filters' map. Should be released after use. */ 27 + static int pinned_filter_idx = -1; 28 28 29 29 static const struct perf_sample_info { 30 30 enum perf_bpf_filter_term type; ··· 52 46 PERF_SAMPLE_TYPE(CODE_PAGE_SIZE, "--code-page-size"), 53 47 PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"), 54 48 }; 49 + 50 + static int get_pinned_fd(const char *name); 55 51 56 52 static const struct perf_sample_info *get_sample_info(enum perf_bpf_filter_term type) 57 53 { ··· 175 167 return tgid; 176 168 } 177 169 178 - static int update_pid_hash(struct sample_filter_bpf *skel, struct evsel *evsel, 179 - struct perf_bpf_filter_entry *entry) 170 + static int update_pid_hash(struct evsel *evsel, struct perf_bpf_filter_entry *entry) 180 171 { 181 172 int filter_idx; 182 - int nr, last; 183 - int fd = bpf_map__fd(skel->maps.filters); 173 + int fd, nr, last; 184 174 struct perf_thread_map *threads; 175 + 176 + fd = get_pinned_fd("filters"); 177 + if (fd < 0) { 178 + pr_debug("cannot get fd for 'filters' map\n"); 179 + return fd; 180 + } 185 181 186 182 /* Find the first available entry in the filters map */ 187 183 for (filter_idx = 0; filter_idx < MAX_FILTERS; filter_idx++) { 188 - if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) == 0) 184 + if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) == 0) { 185 + pinned_filter_idx = filter_idx; 189 186 break; 187 + } 190 188 } 189 + close(fd); 191 190 192 191 if (filter_idx == MAX_FILTERS) { 193 192 pr_err("Too many users for the filter map\n"); ··· 208 193 } 209 194 210 195 /* save the index to a hash map */ 211 - fd = bpf_map__fd(skel->maps.pid_hash); 196 + fd = get_pinned_fd("pid_hash"); 197 + if (fd < 0) 198 + return fd; 212 199 213 200 last = -1; 214 201 nr = perf_thread_map__nr(threads); ··· 231 214 232 215 if (bpf_map_update_elem(fd, &tgid, &filter_idx, BPF_ANY) < 0) { 233 216 pr_err("Failed to update the pid hash\n"); 234 - return -errno; 217 + close(fd); 218 + return -1; 235 219 } 236 220 pr_debug("pid hash: %d -> %d\n", tgid, filter_idx); 237 221 } 222 + close(fd); 238 223 return 0; 239 224 } 240 225 ··· 259 240 goto err; 260 241 } 261 242 262 - skel = sample_filter_bpf__open(); 263 - if (!skel) { 264 - pr_err("Failed to open perf sample-filter BPF skeleton\n"); 265 - ret = -EPERM; 266 - goto err; 267 - } 268 - 269 - if (needs_pid_hash) { 270 - bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); 271 - bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); 272 - skel->rodata->use_pid_hash = 1; 273 - } 274 - 275 - if (sample_filter_bpf__load(skel) < 0) { 276 - pr_err("Failed to load perf sample-filter BPF skeleton\n"); 277 - ret = -EPERM; 278 - goto err; 279 - } 280 - 281 - if (needs_pid_hash) { 282 - /* The filters map is shared among other processes */ 283 - ret = update_pid_hash(skel, evsel, entry); 243 + if (needs_pid_hash && geteuid() != 0) { 244 + /* The filters map is shared among other processes */ 245 + ret = update_pid_hash(evsel, entry); 284 246 if (ret < 0) 285 247 goto err; 286 - } else { 287 - i = 0; 288 - fd = bpf_map__fd(skel->maps.filters); 289 248 290 - /* The filters map has only one entry in this case */ 291 - if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { 292 - ret = -errno; 293 - pr_err("Failed to update the filter map\n"); 249 + fd = get_pinned_fd("perf_sample_filter"); 250 + if (fd < 0) { 251 + ret = fd; 294 252 goto err; 295 253 } 254 + 255 + for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { 256 + for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { 257 + ret = ioctl(FD(evsel, x, y), PERF_EVENT_IOC_SET_BPF, fd); 258 + if (ret < 0) { 259 + pr_err("Failed to attach perf sample-filter\n"); 260 + goto err; 261 + } 262 + } 263 + } 264 + 265 + close(fd); 266 + free(entry); 267 + return 0; 268 + } 269 + 270 + skel = sample_filter_bpf__open_and_load(); 271 + if (!skel) { 272 + ret = -errno; 273 + pr_err("Failed to load perf sample-filter BPF skeleton\n"); 274 + goto err; 275 + } 276 + 277 + i = 0; 278 + fd = bpf_map__fd(skel->maps.filters); 279 + 280 + /* The filters map has only one entry in this case */ 281 + if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { 282 + ret = -errno; 283 + pr_err("Failed to update the filter map\n"); 284 + goto err; 296 285 } 297 286 298 287 prog = skel->progs.perf_sample_filter; ··· 333 306 free(expr); 334 307 } 335 308 sample_filter_bpf__destroy(evsel->bpf_skel); 309 + 310 + if (pinned_filter_idx >= 0) { 311 + int fd = get_pinned_fd("filters"); 312 + 313 + bpf_map_delete_elem(fd, &pinned_filter_idx); 314 + pinned_filter_idx = -1; 315 + close(fd); 316 + } 317 + 336 318 return 0; 337 319 } 338 320 ··· 384 348 perf_bpf_filter_lex_destroy(); 385 349 386 350 return ret; 351 + } 352 + 353 + int perf_bpf_filter__pin(void) 354 + { 355 + struct sample_filter_bpf *skel; 356 + char *path = NULL; 357 + int dir_fd, ret = -1; 358 + 359 + skel = sample_filter_bpf__open(); 360 + if (!skel) { 361 + ret = -errno; 362 + pr_err("Failed to open perf sample-filter BPF skeleton\n"); 363 + goto err; 364 + } 365 + 366 + /* pinned program will use pid-hash */ 367 + bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); 368 + bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); 369 + skel->rodata->use_pid_hash = 1; 370 + 371 + if (sample_filter_bpf__load(skel) < 0) { 372 + ret = -errno; 373 + pr_err("Failed to load perf sample-filter BPF skeleton\n"); 374 + goto err; 375 + } 376 + 377 + if (asprintf(&path, "%s/fs/bpf/%s", sysfs__mountpoint(), 378 + PERF_BPF_FILTER_PIN_PATH) < 0) { 379 + ret = -errno; 380 + pr_err("Failed to allocate pathname in the BPF-fs\n"); 381 + goto err; 382 + } 383 + 384 + ret = bpf_object__pin(skel->obj, path); 385 + if (ret < 0) { 386 + pr_err("Failed to pin BPF filter objects\n"); 387 + goto err; 388 + } 389 + 390 + /* setup access permissions for the pinned objects */ 391 + dir_fd = open(path, O_PATH); 392 + if (dir_fd < 0) { 393 + bpf_object__unpin(skel->obj, path); 394 + ret = dir_fd; 395 + goto err; 396 + } 397 + 398 + /* BPF-fs root has the sticky bit */ 399 + if (fchmodat(dir_fd, "..", 01755, 0) < 0) { 400 + pr_debug("chmod for BPF-fs failed\n"); 401 + ret = -errno; 402 + goto err_close; 403 + } 404 + 405 + /* perf_filter directory */ 406 + if (fchmodat(dir_fd, ".", 0755, 0) < 0) { 407 + pr_debug("chmod for perf_filter directory failed?\n"); 408 + ret = -errno; 409 + goto err_close; 410 + } 411 + 412 + /* programs need write permission for some reason */ 413 + if (fchmodat(dir_fd, "perf_sample_filter", 0777, 0) < 0) { 414 + pr_debug("chmod for perf_sample_filter failed\n"); 415 + ret = -errno; 416 + } 417 + /* maps */ 418 + if (fchmodat(dir_fd, "filters", 0666, 0) < 0) { 419 + pr_debug("chmod for filters failed\n"); 420 + ret = -errno; 421 + } 422 + if (fchmodat(dir_fd, "pid_hash", 0666, 0) < 0) { 423 + pr_debug("chmod for pid_hash failed\n"); 424 + ret = -errno; 425 + } 426 + 427 + err_close: 428 + close(dir_fd); 429 + 430 + err: 431 + free(path); 432 + sample_filter_bpf__destroy(skel); 433 + return ret; 434 + } 435 + 436 + int perf_bpf_filter__unpin(void) 437 + { 438 + struct sample_filter_bpf *skel; 439 + char *path = NULL; 440 + int ret = -1; 441 + 442 + skel = sample_filter_bpf__open_and_load(); 443 + if (!skel) { 444 + ret = -errno; 445 + pr_err("Failed to open perf sample-filter BPF skeleton\n"); 446 + goto err; 447 + } 448 + 449 + if (asprintf(&path, "%s/fs/bpf/%s", sysfs__mountpoint(), 450 + PERF_BPF_FILTER_PIN_PATH) < 0) { 451 + ret = -errno; 452 + pr_err("Failed to allocate pathname in the BPF-fs\n"); 453 + goto err; 454 + } 455 + 456 + ret = bpf_object__unpin(skel->obj, path); 457 + 458 + err: 459 + free(path); 460 + sample_filter_bpf__destroy(skel); 461 + return ret; 462 + } 463 + 464 + static int get_pinned_fd(const char *name) 465 + { 466 + char *path = NULL; 467 + int fd; 468 + 469 + if (asprintf(&path, "%s/fs/bpf/%s/%s", sysfs__mountpoint(), 470 + PERF_BPF_FILTER_PIN_PATH, name) < 0) 471 + return -1; 472 + 473 + fd = bpf_obj_get(path); 474 + 475 + free(path); 476 + return fd; 387 477 }
+13
tools/perf/util/bpf-filter.h
··· 18 18 struct evsel; 19 19 struct target; 20 20 21 + /* path in BPF-fs for the pinned program and maps */ 22 + #define PERF_BPF_FILTER_PIN_PATH "perf_filter" 23 + 21 24 #ifdef HAVE_BPF_SKEL 22 25 struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filter_term term, 23 26 int part, ··· 30 27 int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target); 31 28 int perf_bpf_filter__destroy(struct evsel *evsel); 32 29 u64 perf_bpf_filter__lost_count(struct evsel *evsel); 30 + int perf_bpf_filter__pin(void); 31 + int perf_bpf_filter__unpin(void); 33 32 34 33 #else /* !HAVE_BPF_SKEL */ 35 34 ··· 52 47 static inline u64 perf_bpf_filter__lost_count(struct evsel *evsel __maybe_unused) 53 48 { 54 49 return 0; 50 + } 51 + static inline int perf_bpf_filter__pin(void) 52 + { 53 + return -EOPNOTSUPP; 54 + } 55 + static inline int perf_bpf_filter__unpin(void) 56 + { 57 + return -EOPNOTSUPP; 55 58 } 56 59 #endif /* HAVE_BPF_SKEL*/ 57 60 #endif /* PERF_UTIL_BPF_FILTER_H */