Skip to content

Commit f70976b

Browse files
laoarKernel Patches Daemon
authored and
Kernel Patches Daemon
committed
selftests/bpf: Add selftests for cpumask iter
Within the BPF program, we leverage the cgroup iterator to iterate through percpu runqueue data, specifically the 'nr_running' metric. Subsequently we expose this data to userspace by means of a sequence file. The CPU affinity for the cpumask is determined by the PID of a task: - PID of the init task (PID 1) We typically don't set CPU affinity for init task and thus we can iterate across all possible CPUs. However, in scenarios where you've set CPU affinity for the init task, you should set the cpumask of your current task to full-F. Then proceed to iterate through all possible CPUs using the current task. - PID of a task with defined CPU affinity The aim here is to iterate through a specific cpumask. This scenario aligns with tasks residing within a cpuset cgroup. - Invalid PID (e.g., PID -1) No cpumask is available in this case. The result as follows, #65/1 cpumask_iter/init_pid:OK #65/2 cpumask_iter/invalid_pid:OK #65/3 cpumask_iter/self_pid_one_cpu:OK #65/4 cpumask_iter/self_pid_multi_cpus:OK #65 cpumask_iter:OK Summary: 1/4 PASSED, 0 SKIPPED, 0 FAILED CONFIG_PSI=y is required for this testcase. Signed-off-by: Yafang Shao <[email protected]>
1 parent bc87355 commit f70976b

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed

tools/testing/selftests/bpf/config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ CONFIG_NF_CONNTRACK_MARK=y
7878
CONFIG_NF_DEFRAG_IPV4=y
7979
CONFIG_NF_DEFRAG_IPV6=y
8080
CONFIG_NF_NAT=y
81+
CONFIG_PSI=y
8182
CONFIG_RC_CORE=y
8283
CONFIG_SECURITY=y
8384
CONFIG_SECURITYFS=y
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2024 Yafang Shao <[email protected]> */
3+
4+
#define _GNU_SOURCE
5+
#include <sched.h>
6+
#include <stdio.h>
7+
#include <unistd.h>
8+
9+
#include <test_progs.h>
10+
#include "cgroup_helpers.h"
11+
#include "test_cpumask_iter.skel.h"
12+
13+
static void verify_percpu_data(struct bpf_link *link, int nr_cpu_exp, int nr_running_exp)
14+
{
15+
int iter_fd, len, item, nr_running, psi_running, nr_cpus;
16+
static char buf[128];
17+
size_t left;
18+
char *p;
19+
20+
iter_fd = bpf_iter_create(bpf_link__fd(link));
21+
if (!ASSERT_GE(iter_fd, 0, "iter_fd"))
22+
return;
23+
24+
memset(buf, 0, sizeof(buf));
25+
left = ARRAY_SIZE(buf);
26+
p = buf;
27+
while ((len = read(iter_fd, p, left)) > 0) {
28+
p += len;
29+
left -= len;
30+
}
31+
32+
item = sscanf(buf, "nr_running %u nr_cpus %u psi_running %u\n",
33+
&nr_running, &nr_cpus, &psi_running);
34+
if (nr_cpu_exp == -1) {
35+
ASSERT_EQ(item, -1, "seq_format");
36+
goto out;
37+
}
38+
39+
ASSERT_EQ(item, 3, "seq_format");
40+
ASSERT_GE(nr_running, nr_running_exp, "nr_running");
41+
ASSERT_GE(psi_running, nr_running_exp, "psi_running");
42+
ASSERT_EQ(nr_cpus, nr_cpu_exp, "nr_cpus");
43+
44+
/* read() after iter finishes should be ok. */
45+
if (len == 0)
46+
ASSERT_OK(read(iter_fd, buf, sizeof(buf)), "second_read");
47+
48+
out:
49+
close(iter_fd);
50+
}
51+
52+
void test_cpumask_iter(void)
53+
{
54+
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
55+
int nr_possible, cgrp_fd, pid, err, cnt, i;
56+
struct test_cpumask_iter *skel = NULL;
57+
union bpf_iter_link_info linfo;
58+
int cpu_ids[] = {1, 3, 4, 5};
59+
struct bpf_link *link;
60+
cpu_set_t set;
61+
62+
skel = test_cpumask_iter__open_and_load();
63+
if (!ASSERT_OK_PTR(skel, "test_for_each_cpu__open_and_load"))
64+
return;
65+
66+
if (setup_cgroup_environment())
67+
goto destroy;
68+
69+
/* Utilize the cgroup iter */
70+
cgrp_fd = get_root_cgroup();
71+
if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
72+
goto cleanup;
73+
74+
memset(&linfo, 0, sizeof(linfo));
75+
linfo.cgroup.cgroup_fd = cgrp_fd;
76+
linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY;
77+
opts.link_info = &linfo;
78+
opts.link_info_len = sizeof(linfo);
79+
80+
link = bpf_program__attach_iter(skel->progs.cpu_cgroup, &opts);
81+
if (!ASSERT_OK_PTR(link, "attach_iter"))
82+
goto close_fd;
83+
84+
skel->bss->target_pid = 1;
85+
/* In case init task is set CPU affinity */
86+
err = sched_getaffinity(1, sizeof(set), &set);
87+
if (!ASSERT_OK(err, "setaffinity"))
88+
goto close_fd;
89+
90+
cnt = CPU_COUNT(&set);
91+
nr_possible = bpf_num_possible_cpus();
92+
if (test__start_subtest("init_pid"))
93+
/* curent task is running. */
94+
verify_percpu_data(link, cnt, cnt == nr_possible ? 1 : 0);
95+
96+
skel->bss->target_pid = -1;
97+
if (test__start_subtest("invalid_pid"))
98+
verify_percpu_data(link, -1, -1);
99+
100+
pid = getpid();
101+
skel->bss->target_pid = pid;
102+
CPU_ZERO(&set);
103+
CPU_SET(0, &set);
104+
err = sched_setaffinity(pid, sizeof(set), &set);
105+
if (!ASSERT_OK(err, "setaffinity"))
106+
goto free_link;
107+
108+
if (test__start_subtest("self_pid_one_cpu"))
109+
verify_percpu_data(link, 1, 1);
110+
111+
/* Assume there are at least 8 CPUs on the testbed */
112+
if (nr_possible < 8)
113+
goto free_link;
114+
115+
CPU_ZERO(&set);
116+
/* Set the CPU affinitiy: 1,3-5 */
117+
for (i = 0; i < ARRAY_SIZE(cpu_ids); i++)
118+
CPU_SET(cpu_ids[i], &set);
119+
err = sched_setaffinity(pid, sizeof(set), &set);
120+
if (!ASSERT_OK(err, "setaffinity"))
121+
goto free_link;
122+
123+
if (test__start_subtest("self_pid_multi_cpus"))
124+
verify_percpu_data(link, ARRAY_SIZE(cpu_ids), 1);
125+
126+
free_link:
127+
bpf_link__destroy(link);
128+
close_fd:
129+
close(cgrp_fd);
130+
cleanup:
131+
cleanup_cgroup_environment();
132+
destroy:
133+
test_cpumask_iter__destroy(skel);
134+
}

tools/testing/selftests/bpf/progs/cpumask_common.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) __ksym
5555
u32 bpf_cpumask_any_distribute(const struct cpumask *src) __ksym;
5656
u32 bpf_cpumask_any_and_distribute(const struct cpumask *src1, const struct cpumask *src2) __ksym;
5757
u32 bpf_cpumask_weight(const struct cpumask *cpumask) __ksym;
58+
int bpf_iter_cpumask_new(struct bpf_iter_cpumask *it, const struct cpumask *mask) __ksym;
59+
int *bpf_iter_cpumask_next(struct bpf_iter_cpumask *it) __ksym;
60+
void bpf_iter_cpumask_destroy(struct bpf_iter_cpumask *it) __ksym;
5861

5962
void bpf_rcu_read_lock(void) __ksym;
6063
void bpf_rcu_read_unlock(void) __ksym;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright (c) 2024 Yafang Shao <[email protected]> */
3+
4+
#include "vmlinux.h"
5+
#include <bpf/bpf_helpers.h>
6+
#include <bpf/bpf_tracing.h>
7+
8+
#include "task_kfunc_common.h"
9+
#include "cpumask_common.h"
10+
11+
extern const struct psi_group_cpu system_group_pcpu __ksym __weak;
12+
extern const struct rq runqueues __ksym __weak;
13+
14+
int target_pid;
15+
16+
SEC("iter.s/cgroup")
17+
int BPF_PROG(cpu_cgroup, struct bpf_iter_meta *meta, struct cgroup *cgrp)
18+
{
19+
u32 nr_running = 0, psi_nr_running = 0, nr_cpus = 0;
20+
struct psi_group_cpu *groupc;
21+
struct task_struct *p;
22+
struct rq *rq;
23+
int *cpu;
24+
25+
/* epilogue */
26+
if (cgrp == NULL)
27+
return 0;
28+
29+
bpf_rcu_read_lock();
30+
p = bpf_task_from_pid(target_pid);
31+
if (!p) {
32+
bpf_rcu_read_unlock();
33+
return 1;
34+
}
35+
36+
bpf_for_each(cpumask, cpu, p->cpus_ptr) {
37+
rq = (struct rq *)bpf_per_cpu_ptr(&runqueues, *cpu);
38+
if (!rq)
39+
continue;
40+
nr_running += rq->nr_running;
41+
nr_cpus += 1;
42+
43+
groupc = (struct psi_group_cpu *)bpf_per_cpu_ptr(&system_group_pcpu, *cpu);
44+
if (!groupc)
45+
continue;
46+
psi_nr_running += groupc->tasks[NR_RUNNING];
47+
}
48+
BPF_SEQ_PRINTF(meta->seq, "nr_running %u nr_cpus %u psi_running %u\n",
49+
nr_running, nr_cpus, psi_nr_running);
50+
51+
bpf_task_release(p);
52+
bpf_rcu_read_unlock();
53+
return 0;
54+
}
55+
56+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)