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