Skip to content

Commit

Permalink
selftests/bpf: Add selftest for for_each_cpu
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
laoar authored and Kernel Patches Daemon committed Aug 1, 2023
1 parent 13a4399 commit 3f2881a
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 0 deletions.
137 changes: 137 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/for_each_cpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Yafang Shao <laoar.shao@gmail.com> */

#include <test_progs.h>
#include <bpf/libbpf.h>
#include "cgroup_helpers.h"
#include "test_for_each_cpu.skel.h"

static void verify_percpu_psi_value(struct test_for_each_cpu *skel, int fd, __u32 running, int res)
{
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
union bpf_iter_link_info linfo;
int len, iter_fd, result;
struct bpf_link *link;
static char buf[128];
__u32 nr_running;
size_t left;
char *p;

memset(&linfo, 0, sizeof(linfo));
linfo.cgroup.cgroup_fd = fd;
linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY;
opts.link_info = &linfo;
opts.link_info_len = sizeof(linfo);

link = bpf_program__attach_iter(skel->progs.psi_cgroup, &opts);
if (!ASSERT_OK_PTR(link, "attach_iter"))
return;

iter_fd = bpf_iter_create(bpf_link__fd(link));
if (!ASSERT_GE(iter_fd, 0, "iter_fd"))
goto free_link;

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

ASSERT_EQ(sscanf(buf, "nr_running %u ret %d\n", &nr_running, &result), 2, "seq_format");
ASSERT_EQ(result, res, "for_each_cpu_result");
if (running)
ASSERT_GE(nr_running, running, "nr_running");
else
ASSERT_EQ(nr_running, running, "nr_running");

/* read() after iter finishes should be ok. */
if (len == 0)
ASSERT_OK(read(iter_fd, buf, sizeof(buf)), "second_read");
close(iter_fd);
free_link:
bpf_link__destroy(link);
}

void test_root_cgroup(struct test_for_each_cpu *skel)
{
int cgrp_fd, nr_cpus;

cgrp_fd = get_root_cgroup();
if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
return;

skel->bss->cpu_mask = CPU_MASK_POSSIBLE;
skel->bss->pid = 0;
nr_cpus = bpf_num_possible_cpus();
/* At least current is running */
verify_percpu_psi_value(skel, cgrp_fd, 1, nr_cpus);
close(cgrp_fd);
}

void test_child_cgroup(struct test_for_each_cpu *skel)
{
int cgrp_fd, nr_cpus;

cgrp_fd = create_and_get_cgroup("for_each_cpu");
if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
return;

skel->bss->cpu_mask = CPU_MASK_POSSIBLE;
skel->bss->pid = 0;
nr_cpus = bpf_num_possible_cpus();
/* No tasks in the cgroup */
verify_percpu_psi_value(skel, cgrp_fd, 0, nr_cpus);
close(cgrp_fd);
remove_cgroup("for_each_cpu");
}

void verify_invalid_cpumask(struct test_for_each_cpu *skel, int fd, __u32 cpumask, __u32 pid)
{
DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);

skel->bss->cpu_mask = cpumask;
skel->bss->pid = pid;
verify_percpu_psi_value(skel, fd, 0, -EINVAL);
}

void test_invalid_cpumask(struct test_for_each_cpu *skel)
{
int cgrp_fd;

cgrp_fd = create_and_get_cgroup("for_each_cpu");
if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
return;

verify_invalid_cpumask(skel, cgrp_fd, CPU_MASK_POSSIBLE, 1);
verify_invalid_cpumask(skel, cgrp_fd, CPU_MASK_PRESENT, 1);
verify_invalid_cpumask(skel, cgrp_fd, CPU_MASK_ONLINE, 1);
verify_invalid_cpumask(skel, cgrp_fd, CPU_MASK_TASK, 0);
verify_invalid_cpumask(skel, cgrp_fd, -1, 0);
verify_invalid_cpumask(skel, cgrp_fd, -1, 1);
close(cgrp_fd);
remove_cgroup("for_each_cpu");
}

void test_for_each_cpu(void)
{
struct test_for_each_cpu *skel = NULL;

skel = test_for_each_cpu__open_and_load();
if (!ASSERT_OK_PTR(skel, "test_for_each_cpu__open_and_load"))
return;

if (setup_cgroup_environment())
return;

if (test__start_subtest("psi_system"))
test_root_cgroup(skel);
if (test__start_subtest("psi_cgroup"))
test_child_cgroup(skel);
if (test__start_subtest("invalid_cpumask"))
test_invalid_cpumask(skel);

test_for_each_cpu__destroy(skel);
cleanup_cgroup_environment();
}
63 changes: 63 additions & 0 deletions tools/testing/selftests/bpf/progs/test_for_each_cpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2023 Yafang Shao <laoar.shao@gmail.com> */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

#define __percpu __attribute__((btf_type_tag("percpu")))

enum bpf_cpu_mask_type cpu_mask;
__u32 pid;

struct callback_ctx {
__u32 nr_running;
__u32 id;
};

static uint64_t cgroup_id(struct cgroup *cgrp)
{
return cgrp->kn->id;
}

static int callback(__u32 cpu, void *ctx, const void *ptr)
{
unsigned int tasks[NR_PSI_TASK_COUNTS];
const struct psi_group_cpu *groupc = ptr;
struct callback_ctx *data = ctx;

bpf_probe_read_kernel(&tasks, sizeof(tasks), &groupc->tasks);
data->nr_running += tasks[NR_RUNNING];
return 0;
}

SEC("iter.s/cgroup")
int BPF_PROG(psi_cgroup, struct bpf_iter_meta *meta, struct cgroup *cgrp)
{
struct seq_file *seq = (struct seq_file *)meta->seq;
struct psi_group_cpu __percpu *pcpu_ptr;
struct callback_ctx data;
struct psi_group *psi;
__u64 cg_id;
int ret;

cg_id = cgrp ? cgroup_id(cgrp) : 0;
if (!cg_id)
return 1;

psi = cgrp->psi;
if (!psi)
return 1;

pcpu_ptr = psi->pcpu;
if (!pcpu_ptr)
return 1;

data.nr_running = 0;
data.id = cg_id;
ret = bpf_for_each_cpu(callback, &data, pcpu_ptr, cpu_mask, pid);
BPF_SEQ_PRINTF(seq, "nr_running %d ret %d\n", data.nr_running, ret);

return ret ? 1 : 0;
}

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

0 comments on commit 3f2881a

Please sign in to comment.