xref: /linux/tools/testing/selftests/sched_ext/hotplug.c (revision 5d7f3375cd574fc83f0cd30839b21455c0e79ae2)
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 
19 static bool is_cpu_online(void)
20 {
21 	return file_read_long(online_path) > 0;
22 }
23 
24 static int 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)\n",
32 			online ? "online" : "offline", strerror(errno));
33 
34 	return ret;
35 }
36 
37 static enum scx_test_status setup(void **ctx)
38 {
39 	if (!is_cpu_online())
40 		return SCX_TEST_SKIP;
41 
42 	return SCX_TEST_PASS;
43 }
44 
45 static enum scx_test_status test_hotplug(bool onlining, bool cbs_defined)
46 {
47 	struct hotplug *skel;
48 	struct bpf_link *link;
49 	enum scx_test_status status = SCX_TEST_FAIL;
50 	long kind, code;
51 
52 	SCX_ASSERT(is_cpu_online());
53 
54 	skel = hotplug__open();
55 	SCX_FAIL_IF(!skel, "Failed to open");
56 	SCX_ENUM_INIT(skel);
57 	SCX_FAIL_IF(hotplug__load(skel), "Failed to load skel");
58 
59 	/* Testing the offline -> online path, so go offline before starting */
60 	if (onlining && toggle_online_status(0))
61 		goto out_destroy_skel;
62 
63 	if (cbs_defined) {
64 		kind = SCX_KIND_VAL(SCX_EXIT_UNREG_BPF);
65 		code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) | HOTPLUG_EXIT_RSN;
66 		if (onlining)
67 			code |= HOTPLUG_ONLINING;
68 	} else {
69 		kind = SCX_KIND_VAL(SCX_EXIT_UNREG_KERN);
70 		code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) |
71 		       SCX_ECODE_VAL(SCX_ECODE_RSN_HOTPLUG);
72 	}
73 
74 	if (cbs_defined)
75 		link = bpf_map__attach_struct_ops(skel->maps.hotplug_cb_ops);
76 	else
77 		link = bpf_map__attach_struct_ops(skel->maps.hotplug_nocb_ops);
78 
79 	if (!link) {
80 		SCX_ERR("Failed to attach scheduler");
81 		hotplug__destroy(skel);
82 		return SCX_TEST_FAIL;
83 	}
84 
85 	if (toggle_online_status(onlining ? 1 : 0))
86 		goto out_destroy_link;
87 
88 	if (!SCX_TIMED_WAIT_UEI(skel, uei, 10000)) {
89 		SCX_ERR("Timed out waiting for scheduler to exit");
90 		goto out_destroy_link;
91 	}
92 
93 	SCX_EQ(skel->data->uei.kind, kind);
94 	SCX_EQ(UEI_REPORT(skel, uei), code);
95 
96 	if (!onlining && toggle_online_status(1))
97 		goto out_destroy_link;
98 
99 	status = SCX_TEST_PASS;
100 out_destroy_link:
101 	bpf_link__destroy(link);
102 out_destroy_skel:
103 	hotplug__destroy(skel);
104 
105 	return status;
106 }
107 
108 static enum scx_test_status test_hotplug_attach(void)
109 {
110 	struct hotplug *skel;
111 	struct bpf_link *link;
112 	enum scx_test_status status = SCX_TEST_FAIL;
113 	long kind, code;
114 
115 	SCX_ASSERT(is_cpu_online());
116 	SCX_ASSERT(scx_hotplug_seq() > 0);
117 
118 	skel = SCX_OPS_OPEN(hotplug_nocb_ops, hotplug);
119 	SCX_ASSERT(skel);
120 
121 	SCX_OPS_LOAD(skel, hotplug_nocb_ops, hotplug, uei);
122 
123 	/*
124 	 * Take the CPU offline to increment the global hotplug seq, which
125 	 * should cause attach to fail due to us setting the hotplug seq above
126 	 */
127 	if (toggle_online_status(0))
128 		goto out_destroy_skel;
129 	link = bpf_map__attach_struct_ops(skel->maps.hotplug_nocb_ops);
130 
131 	if (toggle_online_status(1))
132 		goto out_destroy_link;
133 
134 	SCX_ASSERT(link);
135 	if (!SCX_TIMED_WAIT_UEI(skel, uei, 10000)) {
136 		SCX_ERR("Timed out waiting for scheduler to exit");
137 		goto out_destroy_link;
138 	}
139 
140 	kind = SCX_KIND_VAL(SCX_EXIT_UNREG_KERN);
141 	code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) |
142 	       SCX_ECODE_VAL(SCX_ECODE_RSN_HOTPLUG);
143 	SCX_EQ(skel->data->uei.kind, kind);
144 	SCX_EQ(UEI_REPORT(skel, uei), code);
145 
146 	status = SCX_TEST_PASS;
147 out_destroy_link:
148 	bpf_link__destroy(link);
149 out_destroy_skel:
150 	hotplug__destroy(skel);
151 
152 	return status;
153 }
154 
155 static enum scx_test_status run(void *ctx)
156 {
157 
158 #define HP_TEST(__onlining, __cbs_defined) ({				\
159 	if (test_hotplug(__onlining, __cbs_defined) != SCX_TEST_PASS)	\
160 		return SCX_TEST_FAIL;					\
161 })
162 
163 	HP_TEST(true, true);
164 	HP_TEST(false, true);
165 	HP_TEST(true, false);
166 	HP_TEST(false, false);
167 
168 #undef HP_TEST
169 
170 	return test_hotplug_attach();
171 }
172 
173 static void cleanup(void *ctx)
174 {
175 	toggle_online_status(1);
176 }
177 
178 struct scx_test hotplug_test = {
179 	.name = "hotplug",
180 	.description = "Verify hotplug behavior",
181 	.setup = setup,
182 	.run = run,
183 	.cleanup = cleanup,
184 };
185 REGISTER_SCX_TEST(&hotplug_test)
186