From nobody Fri Dec 19 17:18:13 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DA26716F0E1; Fri, 21 Jun 2024 22:24:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719008640; cv=none; b=eFffkbLUVR84OrNmDygjKZ2vvAkEoJ8W2M2OMMr0nZ4ZATM8MJO+l/Xd7Q/2HqMx+IST51m8SaF0UdCwHrQOy20Q5nHfZ3UeVwEIq5R+0CXtcoFvIVvO0tlC9ISR3qqRF2dBWlD0KVnMur88GQugDCrzlRUDT6oM29tkD9KkUiI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719008640; c=relaxed/simple; bh=yA6a1kZaqADIwlQBycW+e66P07E/ZjEUB39NlBGm9g0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IvWVlpOqUaOwRYgRc7EmV9JacUQsC35MumsGA2BwO+jQFoK92HS104jVKpH7VOMSzxpEgIa2Fq8Oddu95lEzmBbJo2KcQqFoGQMZd9D2ycUn8btwmBZ4pvA+NaJMxBuPW0ZRQFtpKBkeNidUf6co6So2PcSJLN7NLxgB2mlt0kg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=m/NSWuwK; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="m/NSWuwK" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2617FC4AF1B; Fri, 21 Jun 2024 22:24:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719008640; bh=yA6a1kZaqADIwlQBycW+e66P07E/ZjEUB39NlBGm9g0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m/NSWuwKz+4xIRUZOXDZEyx17QI6/MjPRo++OeWRiOMzT2H3PHCrcaes0LDnVGaaZ iL/UsCPFx74nCwgJaYTolT0Xvi6YafLboTMOpUlR9q9yJ3tbYSkmsD7UTtzFWs02X0 I7v/6r2v2HpYZ8bF3A3EMWXw5b6+1vnK5gH1rIFxP3VyZl9d0SH9mVH466IjzuE9fO /0NzfSHLhjCwSc2d65Uhnp4tM6jzV1fXSpvt+JksaMeVmol0NZgxNyNwEq5daZL4Nu +PZtvgPYOsoVmGcUau4neClr9t6bnsEyiMWbnjIeuJvbg+JAHo4MUqDw47cCBOw9zm TXJEcz4qY+Vpw== From: Namhyung Kim To: Arnaldo Carvalho de Melo , Ian Rogers , Kan Liang Cc: Jiri Olsa , Adrian Hunter , Peter Zijlstra , Ingo Molnar , LKML , linux-perf-users@vger.kernel.org Subject: [PATCH 4/8] perf bpf-filter: Support pin/unpin BPF object Date: Fri, 21 Jun 2024 15:23:53 -0700 Message-ID: <20240621222357.717374-5-namhyung@kernel.org> X-Mailer: git-send-email 2.45.2.741.gdbec12cfda-goog In-Reply-To: <20240621222357.717374-1-namhyung@kernel.org> References: <20240621222357.717374-1-namhyung@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- tools/perf/util/bpf-filter.c | 230 +++++++++++++++++++++++++++++------ tools/perf/util/bpf-filter.h | 13 ++ 2 files changed, 209 insertions(+), 34 deletions(-) diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index 5ec0e0955ec4..37ed6c48debf 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include +#include +#include +#include =20 #include #include @@ -23,6 +26,9 @@ #define __PERF_SAMPLE_TYPE(tt, st, opt) { tt, #st, opt } #define PERF_SAMPLE_TYPE(_st, opt) __PERF_SAMPLE_TYPE(PBF_TERM_##_st, PERF= _SAMPLE_##_st, opt) =20 +/* Index in the pinned 'filters' map. Should be released after use. */ +static int pinned_filter_idx =3D -1; + static const struct perf_sample_info { enum perf_bpf_filter_term type; const char *name; @@ -47,6 +53,8 @@ static const struct perf_sample_info { PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"), }; =20 +static int get_pinned_fd(const char *name); + static const struct perf_sample_info *get_sample_info(enum perf_bpf_filter= _term type) { size_t i; @@ -167,19 +175,26 @@ static int convert_to_tgid(int tid) return tgid; } =20 -static int update_pid_hash(struct sample_filter_bpf *skel, struct evsel *e= vsel, - struct perf_bpf_filter_entry *entry) +static int update_pid_hash(struct evsel *evsel, struct perf_bpf_filter_ent= ry *entry) { int filter_idx; - int nr, last; - int fd =3D bpf_map__fd(skel->maps.filters); + int fd, nr, last; struct perf_thread_map *threads; =20 + fd =3D get_pinned_fd("filters"); + if (fd < 0) { + pr_debug("cannot get fd for 'filters' map\n"); + return fd; + } + /* Find the first available entry in the filters map */ for (filter_idx =3D 0; filter_idx < MAX_FILTERS; filter_idx++) { - if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) =3D=3D 0) + if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) =3D=3D 0) { + pinned_filter_idx =3D filter_idx; break; + } } + close(fd); =20 if (filter_idx =3D=3D MAX_FILTERS) { pr_err("Too many users for the filter map\n"); @@ -193,7 +208,9 @@ static int update_pid_hash(struct sample_filter_bpf *sk= el, struct evsel *evsel, } =20 /* save the index to a hash map */ - fd =3D bpf_map__fd(skel->maps.pid_hash); + fd =3D get_pinned_fd("pid_hash"); + if (fd < 0) + return fd; =20 last =3D -1; nr =3D perf_thread_map__nr(threads); @@ -214,10 +231,12 @@ static int update_pid_hash(struct sample_filter_bpf *= skel, struct evsel *evsel, =20 if (bpf_map_update_elem(fd, &tgid, &filter_idx, BPF_ANY) < 0) { pr_err("Failed to update the pid hash\n"); - return -errno; + close(fd); + return -1; } pr_debug("pid hash: %d -> %d\n", tgid, filter_idx); } + close(fd); return 0; } =20 @@ -240,40 +259,48 @@ int perf_bpf_filter__prepare(struct evsel *evsel, str= uct target *target) goto err; } =20 - skel =3D sample_filter_bpf__open(); - if (!skel) { - pr_err("Failed to open perf sample-filter BPF skeleton\n"); - ret =3D -EPERM; - goto err; - } + if (needs_pid_hash && geteuid() !=3D 0) { + /* The filters map is shared among other processes */ + ret =3D update_pid_hash(evsel, entry); + if (ret < 0) + goto err; =20 - if (needs_pid_hash) { - bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); - bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); - skel->rodata->use_pid_hash =3D 1; + fd =3D get_pinned_fd("perf_sample_filter"); + if (fd < 0) { + ret =3D fd; + goto err; + } + + for (x =3D 0; x < xyarray__max_x(evsel->core.fd); x++) { + for (y =3D 0; y < xyarray__max_y(evsel->core.fd); y++) { + ret =3D ioctl(FD(evsel, x, y), PERF_EVENT_IOC_SET_BPF, fd); + if (ret < 0) { + pr_err("Failed to attach perf sample-filter\n"); + goto err; + } + } + } + + close(fd); + free(entry); + return 0; } =20 - if (sample_filter_bpf__load(skel) < 0) { + skel =3D sample_filter_bpf__open_and_load(); + if (!skel) { + ret =3D -errno; pr_err("Failed to load perf sample-filter BPF skeleton\n"); - ret =3D -EPERM; goto err; } =20 - if (needs_pid_hash) { - /* The filters map is shared among other processes */ - ret =3D update_pid_hash(skel, evsel, entry); - if (ret < 0) - goto err; - } else { - i =3D 0; - fd =3D bpf_map__fd(skel->maps.filters); - - /* The filters map has only one entry in this case */ - if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { - ret =3D -errno; - pr_err("Failed to update the filter map\n"); - goto err; - } + i =3D 0; + fd =3D bpf_map__fd(skel->maps.filters); + + /* The filters map has only one entry in this case */ + if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { + ret =3D -errno; + pr_err("Failed to update the filter map\n"); + goto err; } =20 prog =3D skel->progs.perf_sample_filter; @@ -306,6 +333,15 @@ int perf_bpf_filter__destroy(struct evsel *evsel) free(expr); } sample_filter_bpf__destroy(evsel->bpf_skel); + + if (pinned_filter_idx >=3D 0) { + int fd =3D get_pinned_fd("filters"); + + bpf_map_delete_elem(fd, &pinned_filter_idx); + pinned_filter_idx =3D -1; + close(fd); + } + return 0; } =20 @@ -349,3 +385,129 @@ int perf_bpf_filter__parse(struct list_head *expr_hea= d, const char *str) =20 return ret; } + +int perf_bpf_filter__pin(void) +{ + struct sample_filter_bpf *skel; + char *path =3D NULL; + int dir_fd, ret =3D -1; + + skel =3D sample_filter_bpf__open(); + if (!skel) { + ret =3D -errno; + pr_err("Failed to open perf sample-filter BPF skeleton\n"); + goto err; + } + + /* pinned program will use pid-hash */ + bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); + bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); + skel->rodata->use_pid_hash =3D 1; + + if (sample_filter_bpf__load(skel) < 0) { + ret =3D -errno; + pr_err("Failed to load perf sample-filter BPF skeleton\n"); + goto err; + } + + if (asprintf(&path, "%s/fs/bpf/%s", sysfs__mountpoint(), + PERF_BPF_FILTER_PIN_PATH) < 0) { + ret =3D -errno; + pr_err("Failed to allocate pathname in the BPF-fs\n"); + goto err; + } + + ret =3D bpf_object__pin(skel->obj, path); + if (ret < 0) { + pr_err("Failed to pin BPF filter objects\n"); + goto err; + } + + /* setup access permissions for the pinned objects */ + dir_fd =3D open(path, O_PATH); + if (dir_fd < 0) { + bpf_object__unpin(skel->obj, path); + ret =3D dir_fd; + goto err; + } + + /* BPF-fs root has the sticky bit */ + if (fchmodat(dir_fd, "..", 01755, 0) < 0) { + pr_debug("chmod for BPF-fs failed\n"); + ret =3D -errno; + goto err_close; + } + + /* perf_filter directory */ + if (fchmodat(dir_fd, ".", 0755, 0) < 0) { + pr_debug("chmod for perf_filter directory failed?\n"); + ret =3D -errno; + goto err_close; + } + + /* programs need write permission for some reason */ + if (fchmodat(dir_fd, "perf_sample_filter", 0777, 0) < 0) { + pr_debug("chmod for perf_sample_filter failed\n"); + ret =3D -errno; + } + /* maps */ + if (fchmodat(dir_fd, "filters", 0666, 0) < 0) { + pr_debug("chmod for filters failed\n"); + ret =3D -errno; + } + if (fchmodat(dir_fd, "pid_hash", 0666, 0) < 0) { + pr_debug("chmod for pid_hash failed\n"); + ret =3D -errno; + } + +err_close: + close(dir_fd); + +err: + free(path); + sample_filter_bpf__destroy(skel); + return ret; +} + +int perf_bpf_filter__unpin(void) +{ + struct sample_filter_bpf *skel; + char *path =3D NULL; + int ret =3D -1; + + skel =3D sample_filter_bpf__open_and_load(); + if (!skel) { + ret =3D -errno; + pr_err("Failed to open perf sample-filter BPF skeleton\n"); + goto err; + } + + if (asprintf(&path, "%s/fs/bpf/%s", sysfs__mountpoint(), + PERF_BPF_FILTER_PIN_PATH) < 0) { + ret =3D -errno; + pr_err("Failed to allocate pathname in the BPF-fs\n"); + goto err; + } + + ret =3D bpf_object__unpin(skel->obj, path); + +err: + free(path); + sample_filter_bpf__destroy(skel); + return ret; +} + +static int get_pinned_fd(const char *name) +{ + char *path =3D NULL; + int fd; + + if (asprintf(&path, "%s/fs/bpf/%s/%s", sysfs__mountpoint(), + PERF_BPF_FILTER_PIN_PATH, name) < 0) + return -1; + + fd =3D bpf_obj_get(path); + + free(path); + return fd; +} diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h index 605a3d0226e0..916ed7770b73 100644 --- a/tools/perf/util/bpf-filter.h +++ b/tools/perf/util/bpf-filter.h @@ -18,6 +18,9 @@ struct perf_bpf_filter_expr { struct evsel; struct target; =20 +/* path in BPF-fs for the pinned program and maps */ +#define PERF_BPF_FILTER_PIN_PATH "perf_filter" + #ifdef HAVE_BPF_SKEL struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filte= r_term term, int part, @@ -27,6 +30,8 @@ int perf_bpf_filter__parse(struct list_head *expr_head, c= onst char *str); int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target); int perf_bpf_filter__destroy(struct evsel *evsel); u64 perf_bpf_filter__lost_count(struct evsel *evsel); +int perf_bpf_filter__pin(void); +int perf_bpf_filter__unpin(void); =20 #else /* !HAVE_BPF_SKEL */ =20 @@ -48,5 +53,13 @@ static inline u64 perf_bpf_filter__lost_count(struct evs= el *evsel __maybe_unused { return 0; } +static inline int perf_bpf_filter__pin(void) +{ + return -EOPNOTSUPP; +} +static inline int perf_bpf_filter__unpin(void) +{ + return -EOPNOTSUPP; +} #endif /* HAVE_BPF_SKEL*/ #endif /* PERF_UTIL_BPF_FILTER_H */ --=20 2.45.2.741.gdbec12cfda-goog