xref: /linux/tools/testing/selftests/sched_ext/hotplug.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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 <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_and_load();
53 	SCX_ASSERT(skel);
54 
55 	/* Testing the offline -> online path, so go offline before starting */
56 	if (onlining)
57 		toggle_online_status(0);
58 
59 	if (cbs_defined) {
60 		kind = SCX_KIND_VAL(SCX_EXIT_UNREG_BPF);
61 		code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) | HOTPLUG_EXIT_RSN;
62 		if (onlining)
63 			code |= HOTPLUG_ONLINING;
64 	} else {
65 		kind = SCX_KIND_VAL(SCX_EXIT_UNREG_KERN);
66 		code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) |
67 		       SCX_ECODE_VAL(SCX_ECODE_RSN_HOTPLUG);
68 	}
69 
70 	if (cbs_defined)
71 		link = bpf_map__attach_struct_ops(skel->maps.hotplug_cb_ops);
72 	else
73 		link = bpf_map__attach_struct_ops(skel->maps.hotplug_nocb_ops);
74 
75 	if (!link) {
76 		SCX_ERR("Failed to attach scheduler");
77 		hotplug__destroy(skel);
78 		return SCX_TEST_FAIL;
79 	}
80 
81 	toggle_online_status(onlining ? 1 : 0);
82 
83 	while (!UEI_EXITED(skel, uei))
84 		sched_yield();
85 
86 	SCX_EQ(skel->data->uei.kind, kind);
87 	SCX_EQ(UEI_REPORT(skel, uei), code);
88 
89 	if (!onlining)
90 		toggle_online_status(1);
91 
92 	bpf_link__destroy(link);
93 	hotplug__destroy(skel);
94 
95 	return SCX_TEST_PASS;
96 }
97 
test_hotplug_attach(void)98 static enum scx_test_status test_hotplug_attach(void)
99 {
100 	struct hotplug *skel;
101 	struct bpf_link *link;
102 	enum scx_test_status status = SCX_TEST_PASS;
103 	long kind, code;
104 
105 	SCX_ASSERT(is_cpu_online());
106 	SCX_ASSERT(scx_hotplug_seq() > 0);
107 
108 	skel = SCX_OPS_OPEN(hotplug_nocb_ops, hotplug);
109 	SCX_ASSERT(skel);
110 
111 	SCX_OPS_LOAD(skel, hotplug_nocb_ops, hotplug, uei);
112 
113 	/*
114 	 * Take the CPU offline to increment the global hotplug seq, which
115 	 * should cause attach to fail due to us setting the hotplug seq above
116 	 */
117 	toggle_online_status(0);
118 	link = bpf_map__attach_struct_ops(skel->maps.hotplug_nocb_ops);
119 
120 	toggle_online_status(1);
121 
122 	SCX_ASSERT(link);
123 	while (!UEI_EXITED(skel, uei))
124 		sched_yield();
125 
126 	kind = SCX_KIND_VAL(SCX_EXIT_UNREG_KERN);
127 	code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) |
128 	       SCX_ECODE_VAL(SCX_ECODE_RSN_HOTPLUG);
129 	SCX_EQ(skel->data->uei.kind, kind);
130 	SCX_EQ(UEI_REPORT(skel, uei), code);
131 
132 	bpf_link__destroy(link);
133 	hotplug__destroy(skel);
134 
135 	return status;
136 }
137 
run(void * ctx)138 static enum scx_test_status run(void *ctx)
139 {
140 
141 #define HP_TEST(__onlining, __cbs_defined) ({				\
142 	if (test_hotplug(__onlining, __cbs_defined) != SCX_TEST_PASS)	\
143 		return SCX_TEST_FAIL;					\
144 })
145 
146 	HP_TEST(true, true);
147 	HP_TEST(false, true);
148 	HP_TEST(true, false);
149 	HP_TEST(false, false);
150 
151 #undef HP_TEST
152 
153 	return test_hotplug_attach();
154 }
155 
cleanup(void * ctx)156 static void cleanup(void *ctx)
157 {
158 	toggle_online_status(1);
159 }
160 
161 struct scx_test hotplug_test = {
162 	.name = "hotplug",
163 	.description = "Verify hotplug behavior",
164 	.setup = setup,
165 	.run = run,
166 	.cleanup = cleanup,
167 };
168 REGISTER_SCX_TEST(&hotplug_test)
169