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