Skip to content

Commit

Permalink
selftests/bpf: Add selftests for cpumask iter
Browse files Browse the repository at this point in the history
Add selftests for the newly added cpumask iter.
- cpumask_iter_success
  - The number of CPUs should be expected when iterating over the cpumask
  - percpu data extracted from the percpu struct should be expected
  - It can work in both non-sleepable and sleepable prog
  - RCU lock is only required by bpf_iter_cpumask_new()
  - It is fine without calling bpf_iter_cpumask_next()

- cpumask_iter_failure
  - RCU lock is required in sleepable prog
  - The cpumask to be iterated over can't be NULL
  - bpf_iter_cpumask_destroy() is required after calling
    bpf_iter_cpumask_new()
  - bpf_iter_cpumask_destroy() can only destroy an initilialized iter
  - bpf_iter_cpumask_next() must use an initilialized iter

The result as follows,

  torvalds#64/37   cpumask/test_cpumask_iter:OK
  torvalds#64/38   cpumask/test_cpumask_iter_sleepable:OK
  torvalds#64/39   cpumask/test_cpumask_iter_sleepable:OK
  torvalds#64/40   cpumask/test_cpumask_iter_next_no_rcu:OK
  torvalds#64/41   cpumask/test_cpumask_iter_no_next:OK
  torvalds#64/42   cpumask/test_cpumask_iter:OK
  torvalds#64/43   cpumask/test_cpumask_iter_no_rcu:OK
  torvalds#64/44   cpumask/test_cpumask_iter_no_destroy:OK
  torvalds#64/45   cpumask/test_cpumask_iter_null_pointer:OK
  torvalds#64/46   cpumask/test_cpumask_iter_next_uninit:OK
  torvalds#64/47   cpumask/test_cpumask_iter_destroy_uninit:OK
  torvalds#64      cpumask:OK

Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
  • Loading branch information
laoar authored and intel-lab-lkp committed Jan 31, 2024
1 parent 92f80f2 commit 0c6c6b2
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 0 deletions.
1 change: 1 addition & 0 deletions tools/testing/selftests/bpf/config
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ CONFIG_NF_CONNTRACK_MARK=y
CONFIG_NF_DEFRAG_IPV4=y
CONFIG_NF_DEFRAG_IPV6=y
CONFIG_NF_NAT=y
CONFIG_PSI=y
CONFIG_RC_CORE=y
CONFIG_SECURITY=y
CONFIG_SECURITYFS=y
Expand Down
152 changes: 152 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/cpumask.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */

#define _GNU_SOURCE
#include <sched.h>

#include <test_progs.h>
#include "cpumask_failure.skel.h"
#include "cpumask_success.skel.h"
#include "cpumask_iter_success.skel.h"
#include "cpumask_iter_failure.skel.h"
#include "cgroup_helpers.h"

static const char * const cpumask_success_testcases[] = {
"test_alloc_free_cpumask",
Expand Down Expand Up @@ -61,6 +67,142 @@ static void verify_success(const char *prog_name)
cpumask_success__destroy(skel);
}

static const char * const cpumask_iter_success_testcases[] = {
"test_cpumask_iter",
"test_cpumask_iter_sleepable",
};

static int read_percpu_data(struct bpf_link *link, int nr_cpu_exp, int nr_running_exp)
{
int iter_fd, len, item, nr_running, psi_running, nr_cpus, err = -1;
char buf[128];
size_t left;
char *p;

iter_fd = bpf_iter_create(bpf_link__fd(link));
if (!ASSERT_GE(iter_fd, 0, "iter_fd"))
return -1;

memset(buf, 0, sizeof(buf));
left = ARRAY_SIZE(buf);
p = buf;
while ((len = read(iter_fd, p, left)) > 0) {
p += len;
left -= len;
}

item = sscanf(buf, "nr_running %u nr_cpus %u psi_running %u\n",
&nr_running, &nr_cpus, &psi_running);
if (!ASSERT_EQ(item, 3, "seq_format"))
goto out;
if (!ASSERT_EQ(nr_cpus, nr_cpu_exp, "nr_cpus"))
goto out;
if (!ASSERT_GE(nr_running, nr_running_exp, "nr_running"))
goto out;
if (!ASSERT_GE(psi_running, nr_running_exp, "psi_running"))
goto out;

err = 0;
out:
close(iter_fd);
return err;
}

static void verify_iter_success(const char *prog_name)
{
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
int cgrp_fd, nr_cpus, err, i, chosen = 0;
struct cpumask_iter_success *skel;
union bpf_iter_link_info linfo;
struct bpf_program *prog;
struct bpf_link *link;
cpu_set_t set;

if (setup_cgroup_environment())
return;

/* Utilize the cgroup iter */
cgrp_fd = get_root_cgroup();
if (!ASSERT_GE(cgrp_fd, 0, "create_cgrp"))
goto cleanup;

skel = cpumask_iter_success__open();
if (!ASSERT_OK_PTR(skel, "cpumask_iter_success__open"))
goto close_fd;

skel->bss->pid = getpid();
nr_cpus = libbpf_num_possible_cpus();

err = cpumask_iter_success__load(skel);
if (!ASSERT_OK(err, "cpumask_iter_success__load"))
goto destroy;

prog = bpf_object__find_program_by_name(skel->obj, prog_name);
if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name"))
goto destroy;

memset(&linfo, 0, sizeof(linfo));
linfo.cgroup.cgroup_fd = cgrp_fd;
linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY;
opts.link_info = &linfo;
opts.link_info_len = sizeof(linfo);
link = bpf_program__attach_iter(prog, &opts);
if (!ASSERT_OK_PTR(link, "bpf_program__attach"))
goto destroy;

/* Case 1): Enable all possible CPUs */
CPU_ZERO(&set);
for (i = 0; i < nr_cpus; i++)
CPU_SET(i, &set);
err = sched_setaffinity(skel->bss->pid, sizeof(set), &set);
if (!ASSERT_OK(err, "setaffinity_all_cpus"))
goto free_link;
err = read_percpu_data(link, nr_cpus, 1);
if (!ASSERT_OK(err, "read_percpu_data"))
goto free_link;
if (!ASSERT_OK(skel->bss->err, "null_rq"))
goto free_link;

/* Case 2): CPU0 only */
CPU_ZERO(&set);
CPU_SET(0, &set);
err = sched_setaffinity(skel->bss->pid, sizeof(set), &set);
if (!ASSERT_OK(err, "setaffinity_cpu0"))
goto free_link;
err = read_percpu_data(link, 1, 1);
if (!ASSERT_OK(err, "read_percpu_data"))
goto free_link;
if (!ASSERT_OK(skel->bss->err, "null_rq_psi"))
goto free_link;

/* Case 3): Partial CPUs */
CPU_ZERO(&set);
for (i = 0; i < nr_cpus; i++) {
if (i < 4 && i & 0x1)
continue;
if (i > 8 && i & 0x2)
continue;
CPU_SET(i, &set);
chosen++;
}
err = sched_setaffinity(skel->bss->pid, sizeof(set), &set);
if (!ASSERT_OK(err, "setaffinity_partial_cpus"))
goto free_link;
err = read_percpu_data(link, chosen, 1);
if (!ASSERT_OK(err, "read_percpu_data"))
goto free_link;
ASSERT_OK(skel->bss->err, "null_rq_psi");

free_link:
bpf_link__destroy(link);
destroy:
cpumask_iter_success__destroy(skel);
close_fd:
close(cgrp_fd);
cleanup:
cleanup_cgroup_environment();
}

void test_cpumask(void)
{
int i;
Expand All @@ -74,4 +216,14 @@ void test_cpumask(void)

RUN_TESTS(cpumask_success);
RUN_TESTS(cpumask_failure);

for (i = 0; i < ARRAY_SIZE(cpumask_iter_success_testcases); i++) {
if (!test__start_subtest(cpumask_iter_success_testcases[i]))
continue;

verify_iter_success(cpumask_iter_success_testcases[i]);
}

RUN_TESTS(cpumask_iter_success);
RUN_TESTS(cpumask_iter_failure);
}
3 changes: 3 additions & 0 deletions tools/testing/selftests/bpf/progs/cpumask_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src) __ksym
u32 bpf_cpumask_any_distribute(const struct cpumask *src) __ksym;
u32 bpf_cpumask_any_and_distribute(const struct cpumask *src1, const struct cpumask *src2) __ksym;
u32 bpf_cpumask_weight(const struct cpumask *cpumask) __ksym;
int bpf_iter_cpumask_new(struct bpf_iter_cpumask *it, const struct cpumask *mask) __ksym;
int *bpf_iter_cpumask_next(struct bpf_iter_cpumask *it) __ksym;
void bpf_iter_cpumask_destroy(struct bpf_iter_cpumask *it) __ksym;

void bpf_rcu_read_lock(void) __ksym;
void bpf_rcu_read_unlock(void) __ksym;
Expand Down
99 changes: 99 additions & 0 deletions tools/testing/selftests/bpf/progs/cpumask_iter_failure.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2024 Yafang Shao <laoar.shao@gmail.com> */

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

#include "bpf_misc.h"
#include "task_kfunc_common.h"
#include "cpumask_common.h"

char _license[] SEC("license") = "GPL";

SEC("iter.s/cgroup")
__failure __msg("R2 must be a rcu pointer")
int BPF_PROG(test_cpumask_iter_no_rcu, struct bpf_iter_meta *meta, struct cgroup *cgrp)
{
struct task_struct *p;
int *cpu;

p = bpf_task_from_pid(1);
if (!p)
return 1;

bpf_for_each(cpumask, cpu, p->cpus_ptr) {
}
bpf_task_release(p);
return 0;
}

SEC("iter/cgroup")
__failure __msg("Possibly NULL pointer passed to trusted arg1")
int BPF_PROG(test_cpumask_iter_null_pointer, struct bpf_iter_meta *meta, struct cgroup *cgrp)
{
struct cpumask *mask = NULL;
int *cpu;

bpf_for_each(cpumask, cpu, mask) {
}
return 0;
}

SEC("iter.s/cgroup")
__failure __msg("Unreleased reference id=3 alloc_insn=10")
int BPF_PROG(test_cpumask_iter_no_destroy, struct bpf_iter_meta *meta, struct cgroup *cgrp)
{
struct bpf_iter_cpumask it;
struct task_struct *p;

p = bpf_task_from_pid(1);
if (!p)
return 1;

bpf_rcu_read_lock();
bpf_iter_cpumask_new(&it, p->cpus_ptr);
bpf_rcu_read_unlock();

bpf_iter_cpumask_next(&it);
bpf_task_release(p);
return 0;
}

SEC("iter/cgroup")
__failure __msg("expected an initialized iter_cpumask as arg #1")
int BPF_PROG(test_cpumask_iter_next_uninit, struct bpf_iter_meta *meta, struct cgroup *cgrp)
{
struct bpf_iter_cpumask *it = NULL;

bpf_iter_cpumask_next(it);
return 0;
}

SEC("iter/cgroup")
__failure __msg("expected an initialized iter_cpumask as arg #1")
int BPF_PROG(test_cpumask_iter_next_uninit2, struct bpf_iter_meta *meta, struct cgroup *cgrp)
{
struct bpf_iter_cpumask it = {};

bpf_iter_cpumask_next(&it);
return 0;
}

SEC("iter/cgroup")
__failure __msg("expected an initialized iter_cpumask as arg #1")
int BPF_PROG(test_cpumask_iter_destroy_uninit, struct bpf_iter_meta *meta, struct cgroup *cgrp)
{
struct bpf_iter_cpumask_kern it = {.cpu = -1};
struct bpf_cpumask *mask;

mask = bpf_cpumask_create();
if (!mask)
return 1;

bpf_cpumask_setall(mask);
it.mask = &mask->cpumask;
bpf_iter_cpumask_destroy((struct bpf_iter_cpumask *)&it);
bpf_cpumask_release(mask);
return 0;
}
Loading

0 comments on commit 0c6c6b2

Please sign in to comment.