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