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