xref: /linux/tools/testing/selftests/sched_ext/hotplug.c (revision b4ada0618eed0fbd1b1630f73deb048c592b06a1)
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 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 
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 
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 
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 
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 
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