Skip to content

Commit 8054e16

Browse files
laoarKernel Patches Daemon
authored andcommitted
selftests/bpf: Add selftest for for_each_cpu
Add selftest for the new for_each_cpu helper. The result: $ tools/testing/selftests/bpf/test_progs --name=for_each_cpu #84/1 for_each_cpu/psi_system:OK #84/2 for_each_cpu/psi_cgroup:OK #84/3 for_each_cpu/invalid_cpumask:OK #84 for_each_cpu:OK Summary: 1/3 PASSED, 0 SKIPPED, 0 FAILED Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
1 parent 8bfd3b5 commit 8054e16

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2023 Yafang Shao <laoar.shao@gmail.com> */
3+
4+
#include <test_progs.h>
5+
#include <bpf/libbpf.h>
6+
#include "cgroup_helpers.h"
7+
#include "test_for_each_cpu.skel.h"
8+
9+
static void verify_percpu_psi_value(struct test_for_each_cpu *skel, int fd, __u32 running, int res)
10+
{
11+
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
12+
union bpf_iter_link_info linfo;
13+
int len, iter_fd, result;
14+
struct bpf_link *link;
15+
static char buf[128];
16+
__u32 nr_running;
17+
size_t left;
18+
char *p;
19+
20+
memset(&linfo, 0, sizeof(linfo));
21+
linfo.cgroup.cgroup_fd = fd;
22+
linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY;
23+
opts.link_info = &linfo;
24+
opts.link_info_len = sizeof(linfo);
25+
26+
link = bpf_program__attach_iter(skel->progs.psi_cgroup, &opts);
27+
if (!ASSERT_OK_PTR(link, "attach_iter"))
28+
return;
29+
30+
iter_fd = bpf_iter_create(bpf_link__fd(link));
31+
if (!ASSERT_GE(iter_fd, 0, "iter_fd"))
32+
goto free_link;
33+
34+
memset(buf, 0, sizeof(buf));
35+
left = ARRAY_SIZE(buf);
36+
p = buf;
37+
while ((len = read(iter_fd, p, left)) > 0) {
38+
p += len;
39+
left -= len;
40+
}
41+
42+
ASSERT_EQ(sscanf(buf, "nr_running %u ret %d\n", &nr_running, &result), 2, "seq_format");
43+
ASSERT_EQ(result, res, "for_each_cpu_result");
44+
if (running)
45+
ASSERT_GE(nr_running, running, "nr_running");
46+
else
47+
ASSERT_EQ(nr_running, running, "nr_running");
48+
49+
/* read() after iter finishes should be ok. */
50+
if (len == 0)
51+
ASSERT_OK(read(iter_fd, buf, sizeof(buf)), "second_read");
52+
close(iter_fd);
53+
free_link:
54+
bpf_link__destroy(link);
55+
}
56+
57+
void test_root_cgroup(struct test_for_each_cpu *skel)
58+
{
59+
int cgrp_fd, nr_cpus;
60+
61+
cgrp_fd = get_root_cgroup();
62+
if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
63+
return;
64+
65+
skel->bss->cpu_mask = CPU_MASK_POSSIBLE;
66+
skel->bss->pid = 0;
67+
nr_cpus = bpf_num_possible_cpus();
68+
/* At least current is running */
69+
verify_percpu_psi_value(skel, cgrp_fd, 1, nr_cpus);
70+
close(cgrp_fd);
71+
}
72+
73+
void test_child_cgroup(struct test_for_each_cpu *skel)
74+
{
75+
int cgrp_fd, nr_cpus;
76+
77+
cgrp_fd = create_and_get_cgroup("for_each_cpu");
78+
if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
79+
return;
80+
81+
skel->bss->cpu_mask = CPU_MASK_POSSIBLE;
82+
skel->bss->pid = 0;
83+
nr_cpus = bpf_num_possible_cpus();
84+
/* No tasks in the cgroup */
85+
verify_percpu_psi_value(skel, cgrp_fd, 0, nr_cpus);
86+
close(cgrp_fd);
87+
remove_cgroup("for_each_cpu");
88+
}
89+
90+
void verify_invalid_cpumask(struct test_for_each_cpu *skel, int fd, __u32 cpumask, __u32 pid)
91+
{
92+
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
93+
94+
skel->bss->cpu_mask = cpumask;
95+
skel->bss->pid = pid;
96+
verify_percpu_psi_value(skel, fd, 0, -EINVAL);
97+
}
98+
99+
void test_invalid_cpumask(struct test_for_each_cpu *skel)
100+
{
101+
int cgrp_fd;
102+
103+
cgrp_fd = create_and_get_cgroup("for_each_cpu");
104+
if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
105+
return;
106+
107+
verify_invalid_cpumask(skel, cgrp_fd, CPU_MASK_POSSIBLE, 1);
108+
verify_invalid_cpumask(skel, cgrp_fd, CPU_MASK_PRESENT, 1);
109+
verify_invalid_cpumask(skel, cgrp_fd, CPU_MASK_ONLINE, 1);
110+
verify_invalid_cpumask(skel, cgrp_fd, CPU_MASK_TASK, 0);
111+
verify_invalid_cpumask(skel, cgrp_fd, -1, 0);
112+
verify_invalid_cpumask(skel, cgrp_fd, -1, 1);
113+
close(cgrp_fd);
114+
remove_cgroup("for_each_cpu");
115+
}
116+
117+
void test_for_each_cpu(void)
118+
{
119+
struct test_for_each_cpu *skel = NULL;
120+
121+
skel = test_for_each_cpu__open_and_load();
122+
if (!ASSERT_OK_PTR(skel, "test_for_each_cpu__open_and_load"))
123+
return;
124+
125+
if (setup_cgroup_environment())
126+
return;
127+
128+
if (test__start_subtest("psi_system"))
129+
test_root_cgroup(skel);
130+
if (test__start_subtest("psi_cgroup"))
131+
test_child_cgroup(skel);
132+
if (test__start_subtest("invalid_cpumask"))
133+
test_invalid_cpumask(skel);
134+
135+
test_for_each_cpu__destroy(skel);
136+
cleanup_cgroup_environment();
137+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright (c) 2023 Yafang Shao <laoar.shao@gmail.com> */
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
7+
#define __percpu __attribute__((btf_type_tag("percpu")))
8+
9+
enum bpf_cpu_mask_type cpu_mask;
10+
__u32 pid;
11+
12+
struct callback_ctx {
13+
__u32 nr_running;
14+
__u32 id;
15+
};
16+
17+
static uint64_t cgroup_id(struct cgroup *cgrp)
18+
{
19+
return cgrp->kn->id;
20+
}
21+
22+
static int callback(__u32 cpu, void *ctx, const void *ptr)
23+
{
24+
unsigned int tasks[NR_PSI_TASK_COUNTS];
25+
const struct psi_group_cpu *groupc = ptr;
26+
struct callback_ctx *data = ctx;
27+
28+
bpf_probe_read_kernel(&tasks, sizeof(tasks), &groupc->tasks);
29+
data->nr_running += tasks[NR_RUNNING];
30+
return 0;
31+
}
32+
33+
SEC("iter.s/cgroup")
34+
int BPF_PROG(psi_cgroup, struct bpf_iter_meta *meta, struct cgroup *cgrp)
35+
{
36+
struct seq_file *seq = (struct seq_file *)meta->seq;
37+
struct psi_group_cpu __percpu *pcpu_ptr;
38+
struct callback_ctx data;
39+
struct psi_group *psi;
40+
__u64 cg_id;
41+
int ret;
42+
43+
cg_id = cgrp ? cgroup_id(cgrp) : 0;
44+
if (!cg_id)
45+
return 1;
46+
47+
psi = cgrp->psi;
48+
if (!psi)
49+
return 1;
50+
51+
pcpu_ptr = psi->pcpu;
52+
if (!pcpu_ptr)
53+
return 1;
54+
55+
data.nr_running = 0;
56+
data.id = cg_id;
57+
ret = bpf_for_each_cpu(callback, &data, pcpu_ptr, cpu_mask, pid);
58+
BPF_SEQ_PRINTF(seq, "nr_running %d ret %d\n", data.nr_running, ret);
59+
60+
return ret ? 1 : 0;
61+
}
62+
63+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)