Skip to content

Commit 7b6abcf

Browse files
Byte-LabAlexei Starovoitov
authored andcommitted
selftests/bpf: Add selftest suite for cpumask kfuncs
A recent patch added a new set of kfuncs for allocating, freeing, manipulating, and querying cpumasks. This patch adds a new 'cpumask' selftest suite which verifies their behavior. Signed-off-by: David Vernet <void@manifault.com> Link: https://lore.kernel.org/r/20230125143816.721952-5-void@manifault.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent a6541f4 commit 7b6abcf

File tree

5 files changed

+741
-0
lines changed

5 files changed

+741
-0
lines changed

tools/testing/selftests/bpf/DENYLIST.s390x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ cgroup_hierarchical_stats # JIT does not support calling kernel f
1313
cgrp_kfunc # JIT does not support calling kernel function
1414
cgrp_local_storage # prog_attach unexpected error: -524 (trampoline)
1515
core_read_macros # unknown func bpf_probe_read#4 (overlapping)
16+
cpumask # JIT does not support calling kernel function
1617
d_path # failed to auto-attach program 'prog_stat': -524 (trampoline)
1718
decap_sanity # JIT does not support calling kernel function (kfunc)
1819
deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
3+
4+
#include <test_progs.h>
5+
#include "cpumask_failure.skel.h"
6+
#include "cpumask_success.skel.h"
7+
8+
static const char * const cpumask_success_testcases[] = {
9+
"test_alloc_free_cpumask",
10+
"test_set_clear_cpu",
11+
"test_setall_clear_cpu",
12+
"test_first_firstzero_cpu",
13+
"test_test_and_set_clear",
14+
"test_and_or_xor",
15+
"test_intersects_subset",
16+
"test_copy_any_anyand",
17+
"test_insert_leave",
18+
"test_insert_remove_release",
19+
"test_insert_kptr_get_release",
20+
};
21+
22+
static void verify_success(const char *prog_name)
23+
{
24+
struct cpumask_success *skel;
25+
struct bpf_program *prog;
26+
struct bpf_link *link = NULL;
27+
pid_t child_pid;
28+
int status;
29+
30+
skel = cpumask_success__open();
31+
if (!ASSERT_OK_PTR(skel, "cpumask_success__open"))
32+
return;
33+
34+
skel->bss->pid = getpid();
35+
skel->bss->nr_cpus = libbpf_num_possible_cpus();
36+
37+
cpumask_success__load(skel);
38+
if (!ASSERT_OK_PTR(skel, "cpumask_success__load"))
39+
goto cleanup;
40+
41+
prog = bpf_object__find_program_by_name(skel->obj, prog_name);
42+
if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name"))
43+
goto cleanup;
44+
45+
link = bpf_program__attach(prog);
46+
if (!ASSERT_OK_PTR(link, "bpf_program__attach"))
47+
goto cleanup;
48+
49+
child_pid = fork();
50+
if (!ASSERT_GT(child_pid, -1, "child_pid"))
51+
goto cleanup;
52+
if (child_pid == 0)
53+
_exit(0);
54+
waitpid(child_pid, &status, 0);
55+
ASSERT_OK(skel->bss->err, "post_wait_err");
56+
57+
cleanup:
58+
bpf_link__destroy(link);
59+
cpumask_success__destroy(skel);
60+
}
61+
62+
void test_cpumask(void)
63+
{
64+
int i;
65+
66+
for (i = 0; i < ARRAY_SIZE(cpumask_success_testcases); i++) {
67+
if (!test__start_subtest(cpumask_success_testcases[i]))
68+
continue;
69+
70+
verify_success(cpumask_success_testcases[i]);
71+
}
72+
73+
RUN_TESTS(cpumask_failure);
74+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
3+
4+
#ifndef _CPUMASK_COMMON_H
5+
#define _CPUMASK_COMMON_H
6+
7+
#include "errno.h"
8+
#include <stdbool.h>
9+
10+
int err;
11+
12+
struct __cpumask_map_value {
13+
struct bpf_cpumask __kptr_ref * cpumask;
14+
};
15+
16+
struct array_map {
17+
__uint(type, BPF_MAP_TYPE_ARRAY);
18+
__type(key, int);
19+
__type(value, struct __cpumask_map_value);
20+
__uint(max_entries, 1);
21+
} __cpumask_map SEC(".maps");
22+
23+
struct bpf_cpumask *bpf_cpumask_create(void) __ksym;
24+
void bpf_cpumask_release(struct bpf_cpumask *cpumask) __ksym;
25+
struct bpf_cpumask *bpf_cpumask_acquire(struct bpf_cpumask *cpumask) __ksym;
26+
struct bpf_cpumask *bpf_cpumask_kptr_get(struct bpf_cpumask **cpumask) __ksym;
27+
u32 bpf_cpumask_first(const struct cpumask *cpumask) __ksym;
28+
u32 bpf_cpumask_first_zero(const struct cpumask *cpumask) __ksym;
29+
void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) __ksym;
30+
void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) __ksym;
31+
bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask) __ksym;
32+
bool bpf_cpumask_test_and_set_cpu(u32 cpu, struct bpf_cpumask *cpumask) __ksym;
33+
bool bpf_cpumask_test_and_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask) __ksym;
34+
void bpf_cpumask_setall(struct bpf_cpumask *cpumask) __ksym;
35+
void bpf_cpumask_clear(struct bpf_cpumask *cpumask) __ksym;
36+
bool bpf_cpumask_and(struct bpf_cpumask *cpumask,
37+
const struct cpumask *src1,
38+
const struct cpumask *src2) __ksym;
39+
void bpf_cpumask_or(struct bpf_cpumask *cpumask,
40+
const struct cpumask *src1,
41+
const struct cpumask *src2) __ksym;
42+
void bpf_cpumask_xor(struct bpf_cpumask *cpumask,
43+
const struct cpumask *src1,
44+
const struct cpumask *src2) __ksym;
45+
bool bpf_cpumask_equal(const struct cpumask *src1, const struct cpumask *src2) __ksym;
46+
bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2) __ksym;
47+
bool bpf_cpumask_subset(const struct cpumask *src1, const struct cpumask *src2) __ksym;
48+
bool bpf_cpumask_empty(const struct cpumask *cpumask) __ksym;
49+
bool bpf_cpumask_full(const struct cpumask *cpumask) __ksym;
50+
void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) __ksym;
51+
u32 bpf_cpumask_any(const struct cpumask *src) __ksym;
52+
u32 bpf_cpumask_any_and(const struct cpumask *src1, const struct cpumask *src2) __ksym;
53+
54+
static inline const struct cpumask *cast(struct bpf_cpumask *cpumask)
55+
{
56+
return (const struct cpumask *)cpumask;
57+
}
58+
59+
static inline struct bpf_cpumask *create_cpumask(void)
60+
{
61+
struct bpf_cpumask *cpumask;
62+
63+
cpumask = bpf_cpumask_create();
64+
if (!cpumask) {
65+
err = 1;
66+
return NULL;
67+
}
68+
69+
if (!bpf_cpumask_empty(cast(cpumask))) {
70+
err = 2;
71+
bpf_cpumask_release(cpumask);
72+
return NULL;
73+
}
74+
75+
return cpumask;
76+
}
77+
78+
static inline struct __cpumask_map_value *cpumask_map_value_lookup(void)
79+
{
80+
u32 key = 0;
81+
82+
return bpf_map_lookup_elem(&__cpumask_map, &key);
83+
}
84+
85+
static inline int cpumask_map_insert(struct bpf_cpumask *mask)
86+
{
87+
struct __cpumask_map_value local, *v;
88+
long status;
89+
struct bpf_cpumask *old;
90+
u32 key = 0;
91+
92+
local.cpumask = NULL;
93+
status = bpf_map_update_elem(&__cpumask_map, &key, &local, 0);
94+
if (status) {
95+
bpf_cpumask_release(mask);
96+
return status;
97+
}
98+
99+
v = bpf_map_lookup_elem(&__cpumask_map, &key);
100+
if (!v) {
101+
bpf_cpumask_release(mask);
102+
return -ENOENT;
103+
}
104+
105+
old = bpf_kptr_xchg(&v->cpumask, mask);
106+
if (old) {
107+
bpf_cpumask_release(old);
108+
return -EEXIST;
109+
}
110+
111+
return 0;
112+
}
113+
114+
#endif /* _CPUMASK_COMMON_H */
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
3+
4+
#include <vmlinux.h>
5+
#include <bpf/bpf_tracing.h>
6+
#include <bpf/bpf_helpers.h>
7+
#include "bpf_misc.h"
8+
9+
#include "cpumask_common.h"
10+
11+
char _license[] SEC("license") = "GPL";
12+
13+
/* Prototype for all of the program trace events below:
14+
*
15+
* TRACE_EVENT(task_newtask,
16+
* TP_PROTO(struct task_struct *p, u64 clone_flags)
17+
*/
18+
19+
SEC("tp_btf/task_newtask")
20+
__failure __msg("Unreleased reference")
21+
int BPF_PROG(test_alloc_no_release, struct task_struct *task, u64 clone_flags)
22+
{
23+
struct bpf_cpumask *cpumask;
24+
25+
cpumask = create_cpumask();
26+
27+
/* cpumask is never released. */
28+
return 0;
29+
}
30+
31+
SEC("tp_btf/task_newtask")
32+
__failure __msg("NULL pointer passed to trusted arg0")
33+
int BPF_PROG(test_alloc_double_release, struct task_struct *task, u64 clone_flags)
34+
{
35+
struct bpf_cpumask *cpumask;
36+
37+
cpumask = create_cpumask();
38+
39+
/* cpumask is released twice. */
40+
bpf_cpumask_release(cpumask);
41+
bpf_cpumask_release(cpumask);
42+
43+
return 0;
44+
}
45+
46+
SEC("tp_btf/task_newtask")
47+
__failure __msg("bpf_cpumask_acquire args#0 expected pointer to STRUCT bpf_cpumask")
48+
int BPF_PROG(test_acquire_wrong_cpumask, struct task_struct *task, u64 clone_flags)
49+
{
50+
struct bpf_cpumask *cpumask;
51+
52+
/* Can't acquire a non-struct bpf_cpumask. */
53+
cpumask = bpf_cpumask_acquire((struct bpf_cpumask *)task->cpus_ptr);
54+
55+
return 0;
56+
}
57+
58+
SEC("tp_btf/task_newtask")
59+
__failure __msg("bpf_cpumask_set_cpu args#1 expected pointer to STRUCT bpf_cpumask")
60+
int BPF_PROG(test_mutate_cpumask, struct task_struct *task, u64 clone_flags)
61+
{
62+
struct bpf_cpumask *cpumask;
63+
64+
/* Can't set the CPU of a non-struct bpf_cpumask. */
65+
bpf_cpumask_set_cpu(0, (struct bpf_cpumask *)task->cpus_ptr);
66+
67+
return 0;
68+
}
69+
70+
SEC("tp_btf/task_newtask")
71+
__failure __msg("Unreleased reference")
72+
int BPF_PROG(test_insert_remove_no_release, struct task_struct *task, u64 clone_flags)
73+
{
74+
struct bpf_cpumask *cpumask;
75+
struct __cpumask_map_value *v;
76+
77+
cpumask = create_cpumask();
78+
if (!cpumask)
79+
return 0;
80+
81+
if (cpumask_map_insert(cpumask))
82+
return 0;
83+
84+
v = cpumask_map_value_lookup();
85+
if (!v)
86+
return 0;
87+
88+
cpumask = bpf_kptr_xchg(&v->cpumask, NULL);
89+
90+
/* cpumask is never released. */
91+
return 0;
92+
}
93+
94+
SEC("tp_btf/task_newtask")
95+
__failure __msg("Unreleased reference")
96+
int BPF_PROG(test_kptr_get_no_release, struct task_struct *task, u64 clone_flags)
97+
{
98+
struct bpf_cpumask *cpumask;
99+
struct __cpumask_map_value *v;
100+
101+
cpumask = create_cpumask();
102+
if (!cpumask)
103+
return 0;
104+
105+
if (cpumask_map_insert(cpumask))
106+
return 0;
107+
108+
v = cpumask_map_value_lookup();
109+
if (!v)
110+
return 0;
111+
112+
cpumask = bpf_cpumask_kptr_get(&v->cpumask);
113+
114+
/* cpumask is never released. */
115+
return 0;
116+
}
117+
118+
SEC("tp_btf/task_newtask")
119+
__failure __msg("NULL pointer passed to trusted arg0")
120+
int BPF_PROG(test_cpumask_null, struct task_struct *task, u64 clone_flags)
121+
{
122+
/* NULL passed to KF_TRUSTED_ARGS kfunc. */
123+
bpf_cpumask_empty(NULL);
124+
125+
return 0;
126+
}

0 commit comments

Comments
 (0)