summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
blob: de23a14b3d408fcd33925d132436941fc60ff1d8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// SPDX-License-Identifier: GPL-2.0
/* Exercise re-entry into call_srcu() from BPF; see progs/rcu_reentry.c. */
#define _GNU_SOURCE
#include <sched.h>
#include <test_progs.h>
#include "task_local_storage_helpers.h"
#include "trace_helpers.h"
#include "rcu_reentry.skel.h"

/* Tiny RCU has no rcu_segcblist_enqueue() to attach to. */
static bool have_attach_target(void)
{
	unsigned long long addr;

	return kallsyms_find("rcu_segcblist_enqueue", &addr) == 0;
}

/* Tiny SRCU stubs out srcu_expedite_current(); Tree SRCU exports it. */
static bool have_tree_srcu(void)
{
	unsigned long long addr;

	return kallsyms_find("srcu_expedite_current", &addr) == 0;
}

void test_rcu_reentry(void)
{
	struct rcu_reentry *skel;
	int err, pidfd = -1, map_fd;
	cpu_set_t set, old_set;
	bool affinity_saved;
	__u64 val = 1;
	int cpu;

	if (!have_attach_target()) {
		test__skip();
		return;
	}

	skel = rcu_reentry__open_and_load();
	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
		return;

	err = rcu_reentry__attach(skel);
	if (!ASSERT_OK(err, "skel_attach"))
		goto out;

	/* Keep the re-entry on a single CPU; a cpuset may exclude CPU 0. */
	affinity_saved = !sched_getaffinity(0, sizeof(old_set), &old_set);
	cpu = sched_getcpu();
	if (!ASSERT_GE(cpu, 0, "getcpu"))
		goto out;
	CPU_ZERO(&set);
	CPU_SET(cpu, &set);
	if (!ASSERT_OK(sched_setaffinity(0, sizeof(set), &set), "setaffinity"))
		goto out;

	pidfd = sys_pidfd_open(getpid(), 0);
	if (!ASSERT_GE(pidfd, 0, "pidfd_open"))
		goto restore;
	map_fd = bpf_map__fd(skel->maps.task_stg);
	err = bpf_map_update_elem(map_fd, &pidfd, &val, BPF_NOEXIST);
	if (!ASSERT_OK(err, "boot_create"))
		goto restore;

	/* Arm the handler for this thread, then trigger call_rcu_tasks_trace(). */
	skel->bss->target_pid = syscall(__NR_gettid);
	err = bpf_map_delete_elem(map_fd, &pidfd);
	if (!ASSERT_OK(err, "boot_delete"))
		goto restore;

	/*
	 * Only Tree SRCU reaches rcu_segcblist_enqueue() from call_srcu(); a
	 * UP+PREEMPT kernel pairs Tree RCU with Tiny SRCU, so the attach
	 * succeeds but nothing fires.  On Tree SRCU it must fire.
	 */
	if (!skel->bss->hits) {
		if (have_tree_srcu())
			ASSERT_GT(skel->bss->hits, 0, "prog_fired");
		else
			test__skip();
		goto restore;
	}
	ASSERT_EQ(skel->bss->get_errs, 0, "nested_storage_get");
	ASSERT_EQ(skel->bss->del_errs, 0, "nested_storage_delete");
restore:
	if (affinity_saved)
		sched_setaffinity(0, sizeof(old_set), &old_set);
out:
	if (pidfd >= 0)
		close(pidfd);
	rcu_reentry__destroy(skel);
}