BPF List
 help / color / mirror / Atom feed
From: Hui Su <sh_def@163.com>
To: bpf@vger.kernel.org
Cc: qmo@kernel.org, ast@kernel.org, daniel@iogearbox.net,
	andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com,
	Hui Su <sh_def@163.com>
Subject: [PATCH bpf 2/2] bpftool: Fix sparse CPU IDs in prog profile
Date: Tue,  8 Sep 2026 00:57:35 +0900	[thread overview]
Message-ID: <20260907155735.2929529-3-sh_def@163.com> (raw)
In-Reply-To: <20260907155735.2929529-1-sh_def@163.com>

bpftool prog profile currently treats the number of possible CPUs as
both the logical CPU ID range and the stride of the perf event array.
That misses valid logical CPUs when the possible CPU mask is sparse, such
as 0,2-3, and can use incorrect PERF_EVENT_ARRAY keys.

Keep the compact possible CPU count for per-CPU result buffers, while
using the actual logical CPU IDs for perf events and the maximum logical
CPU ID plus one as the metric stride in the PERF_EVENT_ARRAY. Keep the
userspace perf file descriptor index separate from the BPF map key. Do
not advance profile_perf_event_cnt for CPUs that return ENODEV, since it
tracks opened userspace perf file descriptors rather than event-array
slots.

Tested:
- Built tools/bpf/bpftool successfully on the host.
- Booted an arm64 QEMU guest with a patched virt device tree reporting
  possible=0,2-3, present=0,2-3, and online=0,2-3.
- Ran both pre-fix and fixed bpftool with cycles and instructions against
  a BTF-enabled fentry target. The pre-fix binary faulted in
  perf_event_alloc(), while the fixed binary reached perf-event setup and
  reported failure to create the instructions event on CPU 0. QEMU did
  not provide a usable hardware PMU runtime result, so no profile counts
  are claimed.

Fixes: 47c09d6a9f67 ("bpftool: Introduce "prog profile" command")
Signed-off-by: Hui Su <sh_def@163.com>
Link: https://lore.kernel.org/bpf/20260813160858.1042834-3-sh_def@163.com/
---
 tools/bpf/bpftool/prog.c                  | 35 ++++++++++++++++-------
 tools/bpf/bpftool/skeleton/profiler.bpf.c |  7 +++--
 2 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index a9f730d407a9..b1f3d1ce5f0f 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -2349,6 +2349,8 @@ static int profile_tgt_fd = -1;
 static char *profile_tgt_name;
 static int *profile_perf_events;
 static int profile_perf_event_cnt;
+static int *profile_cpu_ids;
+static int profile_cpu_id_span;
 
 static void profile_close_perf_events(struct profiler_bpf *obj)
 {
@@ -2358,10 +2360,11 @@ static void profile_close_perf_events(struct profiler_bpf *obj)
 		close(profile_perf_events[i]);
 
 	free(profile_perf_events);
+	profile_perf_events = NULL;
 	profile_perf_event_cnt = 0;
 }
 
-static int profile_open_perf_event(int mid, int cpu, int map_fd)
+static int profile_open_perf_event(int mid, int cpu, int map_key, int map_fd)
 {
 	int pmu_fd;
 
@@ -2371,15 +2374,12 @@ static int profile_open_perf_event(int mid, int cpu, int map_fd)
 		if (errno == ENODEV) {
 			p_info("cpu %d may be offline, skip %s profiling.",
 				cpu, metrics[mid].name);
-			profile_perf_event_cnt++;
 			return 0;
 		}
 		return -1;
 	}
 
-	if (bpf_map_update_elem(map_fd,
-				&profile_perf_event_cnt,
-				&pmu_fd, BPF_ANY) ||
+	if (bpf_map_update_elem(map_fd, &map_key, &pmu_fd, BPF_ANY) ||
 	    ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
 		close(pmu_fd);
 		return -1;
@@ -2391,7 +2391,7 @@ static int profile_open_perf_event(int mid, int cpu, int map_fd)
 
 static int profile_open_perf_events(struct profiler_bpf *obj)
 {
-	unsigned int cpu, m;
+	unsigned int cpu, m, metric_idx = 0;
 	int map_fd;
 
 	profile_perf_events = calloc(
@@ -2411,12 +2411,16 @@ static int profile_open_perf_events(struct profiler_bpf *obj)
 		if (!metrics[m].selected)
 			continue;
 		for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) {
-			if (profile_open_perf_event(m, cpu, map_fd)) {
-				p_err("failed to create event %s on cpu %u",
-				      metrics[m].name, cpu);
+			int cpu_id = profile_cpu_ids[cpu];
+			int map_key = cpu_id + metric_idx * profile_cpu_id_span;
+
+			if (profile_open_perf_event(m, cpu_id, map_key, map_fd)) {
+				p_err("failed to create event %s on cpu %d",
+				      metrics[m].name, cpu_id);
 				return -1;
 			}
 		}
+		metric_idx++;
 	}
 	return 0;
 }
@@ -2430,6 +2434,8 @@ static void profile_print_and_cleanup(void)
 
 	close(profile_tgt_fd);
 	free(profile_tgt_name);
+	free(profile_cpu_ids);
+	profile_cpu_ids = NULL;
 }
 
 static void int_exit(int signo)
@@ -2441,6 +2447,7 @@ static void int_exit(int signo)
 static int do_profile(int argc, char **argv)
 {
 	int num_metric, num_cpu, err = -1;
+	int *cpu_ids = NULL;
 	struct bpf_program *prog;
 	unsigned long duration;
 	char *endptr;
@@ -2471,11 +2478,13 @@ static int do_profile(int argc, char **argv)
 	if (num_metric <= 0)
 		goto out;
 
-	num_cpu = libbpf_num_possible_cpus();
+	num_cpu = get_possible_cpu_ids(&cpu_ids);
 	if (num_cpu <= 0) {
 		p_err("failed to identify number of CPUs");
 		goto out;
 	}
+	profile_cpu_ids = cpu_ids;
+	profile_cpu_id_span = cpu_ids[num_cpu - 1] + 1;
 
 	profile_obj = profiler_bpf__open();
 	if (!profile_obj) {
@@ -2485,9 +2494,11 @@ static int do_profile(int argc, char **argv)
 
 	profile_obj->rodata->num_cpu = num_cpu;
 	profile_obj->rodata->num_metric = num_metric;
+	profile_obj->rodata->cpu_id_span = profile_cpu_id_span;
 
 	/* adjust map sizes */
-	bpf_map__set_max_entries(profile_obj->maps.events, num_metric * num_cpu);
+	bpf_map__set_max_entries(profile_obj->maps.events,
+				 num_metric * profile_cpu_id_span);
 	bpf_map__set_max_entries(profile_obj->maps.fentry_readings, num_metric);
 	bpf_map__set_max_entries(profile_obj->maps.accum_readings, num_metric);
 	bpf_map__set_max_entries(profile_obj->maps.counts, 1);
@@ -2534,6 +2545,8 @@ static int do_profile(int argc, char **argv)
 		profiler_bpf__destroy(profile_obj);
 	close(profile_tgt_fd);
 	free(profile_tgt_name);
+	free(profile_cpu_ids);
+	profile_cpu_ids = NULL;
 	return err;
 }
 
diff --git a/tools/bpf/bpftool/skeleton/profiler.bpf.c b/tools/bpf/bpftool/skeleton/profiler.bpf.c
index f48c783cb9f7..9486498f7605 100644
--- a/tools/bpf/bpftool/skeleton/profiler.bpf.c
+++ b/tools/bpf/bpftool/skeleton/profiler.bpf.c
@@ -10,7 +10,7 @@ struct bpf_perf_event_value___local {
 	__u64 running;
 } __attribute__((preserve_access_index));
 
-/* map of perf event fds, num_cpu * num_metric entries */
+/* map of perf event fds, cpu_id_span * num_metric entries */
 struct {
 	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
 	__uint(key_size, sizeof(u32));
@@ -40,6 +40,7 @@ struct {
 
 const volatile __u32 num_cpu = 1;
 const volatile __u32 num_metric = 1;
+const volatile __u32 cpu_id_span = 1;
 #define MAX_NUM_METRICS 4
 
 SEC("fentry/XXX")
@@ -67,7 +68,7 @@ int BPF_PROG(fentry_XXX)
 		if (err)
 			return 0;
 		*(ptrs[i]) = reading;
-		key += num_cpu;
+		key += cpu_id_span;
 	}
 
 	return 0;
@@ -107,7 +108,7 @@ int BPF_PROG(fexit_XXX)
 
 	/* read all events before updating the maps, to reduce error */
 	for (i = 0; i < num_metric && i < MAX_NUM_METRICS; i++) {
-		err = bpf_perf_event_read_value(&events, cpu + i * num_cpu,
+		err = bpf_perf_event_read_value(&events, cpu + i * cpu_id_span,
 						(void *)(readings + i),
 						sizeof(*readings));
 		if (err)
-- 
2.54.0


      parent reply	other threads:[~2026-09-07 15:58 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-07 15:57 [PATCH bpf 0/2] bpftool: fix sparse logical CPU handling Hui Su
2026-09-07 15:57 ` [PATCH bpf 1/2] bpftool: Fix CPU IDs in per-CPU map output Hui Su
2026-09-07 15:57 ` Hui Su [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260907155735.2929529-3-sh_def@163.com \
    --to=sh_def@163.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=memxor@gmail.com \
    --cc=qmo@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox