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