1 // SPDX-License-Identifier: GPL-2.0
2
3 #define _GNU_SOURCE
4 #include <sched.h>
5
6 #include <test_progs.h>
7 #include "rhash_timer.skel.h"
8
9 #define MAX_ATTEMPTS 256
10 #define RCU_SYNC_INTERVAL 64
11
pin_to_first_cpu(cpu_set_t * old_mask)12 static int pin_to_first_cpu(cpu_set_t *old_mask)
13 {
14 cpu_set_t new_mask;
15 int cpu;
16
17 if (sched_getaffinity(0, sizeof(*old_mask), old_mask))
18 return -errno;
19
20 for (cpu = 0; cpu < CPU_SETSIZE; cpu++)
21 if (CPU_ISSET(cpu, old_mask))
22 break;
23 if (cpu == CPU_SETSIZE)
24 return -EINVAL;
25
26 CPU_ZERO(&new_mask);
27 CPU_SET(cpu, &new_mask);
28 if (sched_setaffinity(0, sizeof(new_mask), &new_mask))
29 return -errno;
30 return 0;
31 }
32
update_timer_map(int map_fd,__u64 key)33 static int update_timer_map(int map_fd, __u64 key)
34 {
35 __u64 value[3] = {};
36
37 return bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST);
38 }
39
run_prog(int prog_fd,struct bpf_test_run_opts * opts)40 static int run_prog(int prog_fd, struct bpf_test_run_opts *opts)
41 {
42 int err;
43
44 err = bpf_prog_test_run_opts(prog_fd, opts);
45 if (err)
46 return err;
47 return opts->retval;
48 }
49
test_rhash_timer(void)50 void test_rhash_timer(void)
51 {
52 LIBBPF_OPTS(bpf_test_run_opts, opts);
53 struct rhash_timer *skel = NULL;
54 cpu_set_t old_mask;
55 int map_fd = -1, arm_fd, cancel_fd;
56 bool affinity_set = false;
57 __u64 key = 1;
58 int attempt, err;
59
60 err = pin_to_first_cpu(&old_mask);
61 if (!ASSERT_OK(err, "pin_to_first_cpu"))
62 return;
63 affinity_set = true;
64
65 skel = rhash_timer__open_and_load();
66 if (!ASSERT_OK_PTR(skel, "open_and_load"))
67 goto out;
68
69 map_fd = bpf_map__fd(skel->maps.timer_map);
70 if (!ASSERT_GE(map_fd, 0, "timer_map fd"))
71 goto out;
72 arm_fd = bpf_program__fd(skel->progs.arm_deleted_timer);
73 if (!ASSERT_GE(arm_fd, 0, "arm_deleted_timer fd"))
74 goto out;
75 cancel_fd = bpf_program__fd(skel->progs.cancel_recycled_timer);
76 if (!ASSERT_GE(cancel_fd, 0, "cancel_recycled_timer fd"))
77 goto out;
78
79 err = update_timer_map(map_fd, key);
80 if (!ASSERT_OK(err, "seed_timer_map"))
81 goto out;
82
83 for (attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
84 err = run_prog(arm_fd, &opts);
85 if (err) {
86 ASSERT_OK(err, "arm_deleted_timer");
87 goto out;
88 }
89 if (skel->bss->armed != attempt + 1) {
90 ASSERT_EQ(skel->bss->armed, attempt + 1, "armed");
91 goto out;
92 }
93 if (skel->bss->timer_init_err) {
94 ASSERT_OK(skel->bss->timer_init_err, "timer_init_err");
95 goto out;
96 }
97 if (skel->bss->timer_set_callback_err) {
98 ASSERT_OK(skel->bss->timer_set_callback_err,
99 "timer_set_callback_err");
100 goto out;
101 }
102 if (skel->bss->timer_start_err) {
103 ASSERT_OK(skel->bss->timer_start_err, "timer_start_err");
104 goto out;
105 }
106
107 if ((attempt + 1) % RCU_SYNC_INTERVAL == 0) {
108 err = kern_sync_rcu();
109 if (err) {
110 ASSERT_OK(err, "kern_sync_rcu");
111 goto out;
112 }
113 }
114
115 err = update_timer_map(map_fd, ++key);
116 if (err) {
117 ASSERT_OK(err, "replace_timer_map");
118 goto out;
119 }
120
121 err = run_prog(cancel_fd, &opts);
122 if (err) {
123 ASSERT_OK(err, "cancel_recycled_timer");
124 goto out;
125 }
126 if (skel->bss->timer_cancel_err) {
127 ASSERT_OK(skel->bss->timer_cancel_err, "timer_cancel_err");
128 goto out;
129 }
130 if (skel->bss->cancelled)
131 break;
132 }
133
134 ASSERT_GT(skel->bss->cancelled, 0, "preserved timer");
135 out:
136 if (map_fd >= 0)
137 bpf_map_delete_elem(map_fd, &key);
138 rhash_timer__destroy(skel);
139 if (affinity_set)
140 sched_setaffinity(0, sizeof(old_mask), &old_mask);
141 }
142