| /* SPDX-License-Identifier: GPL-2.0 */ | |
| /* | |
| * Copyright (c) 2024 Meta Platforms, Inc. and affiliates. | |
| * Copyright (c) 2024 David Vernet <dvernet@meta.com> | |
| */ | |
| #include <bpf/bpf.h> | |
| #include <sched.h> | |
| #include <scx/common.h> | |
| #include <sys/wait.h> | |
| #include <unistd.h> | |
| #include "hotplug_test.h" | |
| #include "hotplug.bpf.skel.h" | |
| #include "scx_test.h" | |
| #include "util.h" | |
| const char *online_path = "/sys/devices/system/cpu/cpu1/online"; | |
| static bool is_cpu_online(void) | |
| { | |
| return file_read_long(online_path) > 0; | |
| } | |
| static void toggle_online_status(bool online) | |
| { | |
| long val = online ? 1 : 0; | |
| int ret; | |
| ret = file_write_long(online_path, val); | |
| if (ret != 0) | |
| fprintf(stderr, "Failed to bring CPU %s (%s)", | |
| online ? "online" : "offline", strerror(errno)); | |
| } | |
| static enum scx_test_status setup(void **ctx) | |
| { | |
| if (!is_cpu_online()) | |
| return SCX_TEST_SKIP; | |
| return SCX_TEST_PASS; | |
| } | |
| static enum scx_test_status test_hotplug(bool onlining, bool cbs_defined) | |
| { | |
| struct hotplug *skel; | |
| struct bpf_link *link; | |
| long kind, code; | |
| SCX_ASSERT(is_cpu_online()); | |
| skel = hotplug__open(); | |
| SCX_FAIL_IF(!skel, "Failed to open"); | |
| SCX_ENUM_INIT(skel); | |
| SCX_FAIL_IF(hotplug__load(skel), "Failed to load skel"); | |
| /* Testing the offline -> online path, so go offline before starting */ | |
| if (onlining) | |
| toggle_online_status(0); | |
| if (cbs_defined) { | |
| kind = SCX_KIND_VAL(SCX_EXIT_UNREG_BPF); | |
| code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) | HOTPLUG_EXIT_RSN; | |
| if (onlining) | |
| code |= HOTPLUG_ONLINING; | |
| } else { | |
| kind = SCX_KIND_VAL(SCX_EXIT_UNREG_KERN); | |
| code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) | | |
| SCX_ECODE_VAL(SCX_ECODE_RSN_HOTPLUG); | |
| } | |
| if (cbs_defined) | |
| link = bpf_map__attach_struct_ops(skel->maps.hotplug_cb_ops); | |
| else | |
| link = bpf_map__attach_struct_ops(skel->maps.hotplug_nocb_ops); | |
| if (!link) { | |
| SCX_ERR("Failed to attach scheduler"); | |
| hotplug__destroy(skel); | |
| return SCX_TEST_FAIL; | |
| } | |
| toggle_online_status(onlining ? 1 : 0); | |
| while (!UEI_EXITED(skel, uei)) | |
| sched_yield(); | |
| SCX_EQ(skel->data->uei.kind, kind); | |
| SCX_EQ(UEI_REPORT(skel, uei), code); | |
| if (!onlining) | |
| toggle_online_status(1); | |
| bpf_link__destroy(link); | |
| hotplug__destroy(skel); | |
| return SCX_TEST_PASS; | |
| } | |
| static enum scx_test_status test_hotplug_attach(void) | |
| { | |
| struct hotplug *skel; | |
| struct bpf_link *link; | |
| enum scx_test_status status = SCX_TEST_PASS; | |
| long kind, code; | |
| SCX_ASSERT(is_cpu_online()); | |
| SCX_ASSERT(scx_hotplug_seq() > 0); | |
| skel = SCX_OPS_OPEN(hotplug_nocb_ops, hotplug); | |
| SCX_ASSERT(skel); | |
| SCX_OPS_LOAD(skel, hotplug_nocb_ops, hotplug, uei); | |
| /* | |
| * Take the CPU offline to increment the global hotplug seq, which | |
| * should cause attach to fail due to us setting the hotplug seq above | |
| */ | |
| toggle_online_status(0); | |
| link = bpf_map__attach_struct_ops(skel->maps.hotplug_nocb_ops); | |
| toggle_online_status(1); | |
| SCX_ASSERT(link); | |
| while (!UEI_EXITED(skel, uei)) | |
| sched_yield(); | |
| kind = SCX_KIND_VAL(SCX_EXIT_UNREG_KERN); | |
| code = SCX_ECODE_VAL(SCX_ECODE_ACT_RESTART) | | |
| SCX_ECODE_VAL(SCX_ECODE_RSN_HOTPLUG); | |
| SCX_EQ(skel->data->uei.kind, kind); | |
| SCX_EQ(UEI_REPORT(skel, uei), code); | |
| bpf_link__destroy(link); | |
| hotplug__destroy(skel); | |
| return status; | |
| } | |
| static enum scx_test_status run(void *ctx) | |
| { | |
| #define HP_TEST(__onlining, __cbs_defined) ({ \ | |
| if (test_hotplug(__onlining, __cbs_defined) != SCX_TEST_PASS) \ | |
| return SCX_TEST_FAIL; \ | |
| }) | |
| HP_TEST(true, true); | |
| HP_TEST(false, true); | |
| HP_TEST(true, false); | |
| HP_TEST(false, false); | |
| #undef HP_TEST | |
| return test_hotplug_attach(); | |
| } | |
| static void cleanup(void *ctx) | |
| { | |
| toggle_online_status(1); | |
| } | |
| struct scx_test hotplug_test = { | |
| .name = "hotplug", | |
| .description = "Verify hotplug behavior", | |
| .setup = setup, | |
| .run = run, | |
| .cleanup = cleanup, | |
| }; | |
| REGISTER_SCX_TEST(&hotplug_test) |