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 20 static bool is_cpu_online(void) 21 { 22 return file_read_long(online_path) > 0; 23 } 24 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 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 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 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 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 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