1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
4 * Copyright (c) 2024 David Vernet <[email protected]>
5 */
6 #include <bpf/bpf.h>
7 #include <sched.h>
8 #include <scx/common.h>
9 #include <sched.h>
10 #include <sys/wait.h>
11 #include <unistd.h>
12
13 #include "hotplug_test.h"
14 #include "hotplug.bpf.skel.h"
15 #include "scx_test.h"
16 #include "util.h"
17
18 const char *online_path = "/sys/devices/system/cpu/cpu1/online";
19
is_cpu_online(void)20 static bool is_cpu_online(void)
21 {
22 return file_read_long(online_path) > 0;
23 }
24
toggle_online_status(bool online)25 static void toggle_online_status(bool online)
26 {
27 long val = online ? 1 : 0;
28 int ret;
29
30 ret = file_write_long(online_path, val);
31 if (ret != 0)
32 fprintf(stderr, "Failed to bring CPU %s (%s)",
33 online ? "online" : "offline", strerror(errno));
34 }
35
setup(void ** ctx)36 static enum scx_test_status setup(void **ctx)
37 {
38 if (!is_cpu_online())
39 return SCX_TEST_SKIP;
40
41 return SCX_TEST_PASS;
42 }
43
test_hotplug(bool onlining,bool cbs_defined)44 static enum scx_test_status test_hotplug(bool onlining, bool cbs_defined)
45 {
46 struct hotplug *skel;
47 struct bpf_link *link;
48 long kind, code;
49
50 SCX_ASSERT(is_cpu_online());
51
52 skel = hotplug__open();
53 SCX_FAIL_IF(!skel, "Failed to open");
54 SCX_ENUM_INIT(skel);
55 SCX_FAIL_IF(hotplug__load(skel), "Failed to load skel");
56
57 /* Testing the offline -> online path, so go offline before starting */
58 if (onlining)
59 toggle_online_status(0);
60
61 if (cbs_defined) {
62 kind = SCX_KIND_VAL(SCX_EXIT_UNREG_BPF);
63 code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) | HOTPLUG_EXIT_RSN;
64 if (onlining)
65 code |= HOTPLUG_ONLINING;
66 } else {
67 kind = SCX_KIND_VAL(SCX_EXIT_UNREG_KERN);
68 code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) |
69 SCX_ECODE_VAL(SCX_ECODE_RSN_HOTPLUG);
70 }
71
72 if (cbs_defined)
73 link = bpf_map__attach_struct_ops(skel->maps.hotplug_cb_ops);
74 else
75 link = bpf_map__attach_struct_ops(skel->maps.hotplug_nocb_ops);
76
77 if (!link) {
78 SCX_ERR("Failed to attach scheduler");
79 hotplug__destroy(skel);
80 return SCX_TEST_FAIL;
81 }
82
83 toggle_online_status(onlining ? 1 : 0);
84
85 while (!UEI_EXITED(skel, uei))
86 sched_yield();
87
88 SCX_EQ(skel->data->uei.kind, kind);
89 SCX_EQ(UEI_REPORT(skel, uei), code);
90
91 if (!onlining)
92 toggle_online_status(1);
93
94 bpf_link__destroy(link);
95 hotplug__destroy(skel);
96
97 return SCX_TEST_PASS;
98 }
99
test_hotplug_attach(void)100 static enum scx_test_status test_hotplug_attach(void)
101 {
102 struct hotplug *skel;
103 struct bpf_link *link;
104 enum scx_test_status status = SCX_TEST_PASS;
105 long kind, code;
106
107 SCX_ASSERT(is_cpu_online());
108 SCX_ASSERT(scx_hotplug_seq() > 0);
109
110 skel = SCX_OPS_OPEN(hotplug_nocb_ops, hotplug);
111 SCX_ASSERT(skel);
112
113 SCX_OPS_LOAD(skel, hotplug_nocb_ops, hotplug, uei);
114
115 /*
116 * Take the CPU offline to increment the global hotplug seq, which
117 * should cause attach to fail due to us setting the hotplug seq above
118 */
119 toggle_online_status(0);
120 link = bpf_map__attach_struct_ops(skel->maps.hotplug_nocb_ops);
121
122 toggle_online_status(1);
123
124 SCX_ASSERT(link);
125 while (!UEI_EXITED(skel, uei))
126 sched_yield();
127
128 kind = SCX_KIND_VAL(SCX_EXIT_UNREG_KERN);
129 code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) |
130 SCX_ECODE_VAL(SCX_ECODE_RSN_HOTPLUG);
131 SCX_EQ(skel->data->uei.kind, kind);
132 SCX_EQ(UEI_REPORT(skel, uei), code);
133
134 bpf_link__destroy(link);
135 hotplug__destroy(skel);
136
137 return status;
138 }
139
run(void * ctx)140 static enum scx_test_status run(void *ctx)
141 {
142
143 #define HP_TEST(__onlining, __cbs_defined) ({ \
144 if (test_hotplug(__onlining, __cbs_defined) != SCX_TEST_PASS) \
145 return SCX_TEST_FAIL; \
146 })
147
148 HP_TEST(true, true);
149 HP_TEST(false, true);
150 HP_TEST(true, false);
151 HP_TEST(false, false);
152
153 #undef HP_TEST
154
155 return test_hotplug_attach();
156 }
157
cleanup(void * ctx)158 static void cleanup(void *ctx)
159 {
160 toggle_online_status(1);
161 }
162
163 struct scx_test hotplug_test = {
164 .name = "hotplug",
165 .description = "Verify hotplug behavior",
166 .setup = setup,
167 .run = run,
168 .cleanup = cleanup,
169 };
170 REGISTER_SCX_TEST(&hotplug_test)
171