This repository has been archived by the owner on Jun 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
scx: Add selftests for hotplug operations
We've recently added some logic related to hotplug: - If a hotplug event occurs and a scheduler hasn't implemented a callback for it, we automatically exit the scheduler with specific, built-in exit codes - With scx_bpf_exit(), a scheduler can choose to manually exit the scheduler in a hotplug event, or do something else. In any case, the scheduler should _not_ be automatically exited by the kernel Let's add selftests to validate these conditions. Signed-off-by: David Vernet <[email protected]>
- Loading branch information
Showing
7 changed files
with
310 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. | ||
* Copyright (c) 2024 David Vernet <[email protected]> | ||
*/ | ||
|
||
#include <scx/common.bpf.h> | ||
|
||
char _license[] SEC("license") = "GPL"; | ||
|
||
#include "hotplug_test.h" | ||
|
||
UEI_DEFINE(uei); | ||
|
||
void BPF_STRUCT_OPS(hotplug_exit, struct scx_exit_info *ei) | ||
{ | ||
UEI_RECORD(uei, ei); | ||
} | ||
|
||
static void exit_from_hotplug(s32 cpu, bool onlining) | ||
{ | ||
s64 code = SCX_ECODE_ACT_RESTART | HOTPLUG_EXIT_RSN; | ||
|
||
if (onlining) | ||
code |= HOTPLUG_ONLINING; | ||
|
||
scx_bpf_exit(code, "hotplug event detected (%d going %s)", cpu, | ||
onlining ? "online" : "offline"); | ||
} | ||
|
||
void BPF_STRUCT_OPS(hotplug_cpu_online, s32 cpu) | ||
{ | ||
exit_from_hotplug(cpu, true); | ||
} | ||
|
||
void BPF_STRUCT_OPS(hotplug_cpu_offline, s32 cpu) | ||
{ | ||
exit_from_hotplug(cpu, false); | ||
} | ||
|
||
SEC(".struct_ops.link") | ||
struct sched_ext_ops hotplug_cb_ops = { | ||
.cpu_online = hotplug_cpu_online, | ||
.cpu_offline = hotplug_cpu_offline, | ||
.exit = hotplug_exit, | ||
.name = "hotplug_cbs", | ||
.timeout_ms = 1000U, | ||
}; | ||
|
||
SEC(".struct_ops.link") | ||
struct sched_ext_ops hotplug_nocb_ops = { | ||
.exit = hotplug_exit, | ||
.name = "hotplug_nocbs", | ||
.timeout_ms = 1000U, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. | ||
* Copyright (c) 2024 David Vernet <[email protected]> | ||
*/ | ||
#include <bpf/bpf.h> | ||
#include <sched.h> | ||
#include <scx/common.h> | ||
#include <sched.h> | ||
#include <sys/wait.h> | ||
#include <unistd.h> | ||
|
||
#include "hotplug_test.h" | ||
#include "hotplug.bpf.skel.h" | ||
#include "scx_test.h" | ||
#include "util.h" | ||
|
||
struct hotplug *skel; | ||
|
||
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; | ||
|
||
skel = hotplug__open_and_load(); | ||
if (!skel) { | ||
SCX_ERR("Failed to open and load hotplug skel"); | ||
return SCX_TEST_FAIL; | ||
} | ||
|
||
return SCX_TEST_PASS; | ||
} | ||
|
||
static enum scx_test_status test_hotplug(bool onlining, bool cbs_defined) | ||
{ | ||
struct bpf_link *link; | ||
long kind, code; | ||
|
||
SCX_ASSERT(is_cpu_online()); | ||
|
||
/* 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"); | ||
return SCX_TEST_FAIL; | ||
} | ||
|
||
toggle_online_status(onlining ? 1 : 0); | ||
|
||
while (!UEI_EXITED(skel, uei)) | ||
sched_yield(); | ||
|
||
SCX_EQ(UEI_KIND(skel, uei), kind); | ||
SCX_EQ(UEI_ECODE(skel, uei), code); | ||
|
||
if (!onlining) | ||
toggle_online_status(1); | ||
|
||
bpf_link__destroy(link); | ||
|
||
UEI_RESET(skel, uei); | ||
|
||
return SCX_TEST_PASS; | ||
} | ||
|
||
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 SCX_TEST_PASS; | ||
} | ||
|
||
static void cleanup(void *ctx) | ||
{ | ||
hotplug__destroy(skel); | ||
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. | ||
* Copyright (c) 2024 David Vernet <[email protected]> | ||
*/ | ||
|
||
#ifndef __HOTPLUG_TEST_H__ | ||
#define __HOTPLUG_TEST_H__ | ||
|
||
enum hotplug_test_flags { | ||
HOTPLUG_EXIT_RSN = 1LLU << 0, | ||
HOTPLUG_ONLINING = 1LLU << 1, | ||
}; | ||
|
||
#endif // # __HOTPLUG_TEST_H__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. | ||
* Copyright (c) 2024 David Vernet <[email protected]> | ||
*/ | ||
#include <errno.h> | ||
#include <fcntl.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
|
||
/* Returns read len on success, or -errno on failure. */ | ||
static ssize_t read_text(const char *path, char *buf, size_t max_len) | ||
{ | ||
ssize_t len; | ||
int fd; | ||
|
||
fd = open(path, O_RDONLY); | ||
if (fd < 0) | ||
return -errno; | ||
|
||
len = read(fd, buf, max_len - 1); | ||
|
||
if (len >= 0) | ||
buf[len] = 0; | ||
|
||
close(fd); | ||
return len < 0 ? -errno : len; | ||
} | ||
|
||
/* Returns written len on success, or -errno on failure. */ | ||
static ssize_t write_text(const char *path, char *buf, ssize_t len) | ||
{ | ||
int fd; | ||
ssize_t written; | ||
|
||
fd = open(path, O_WRONLY | O_APPEND); | ||
if (fd < 0) | ||
return -errno; | ||
|
||
written = write(fd, buf, len); | ||
close(fd); | ||
return written < 0 ? -errno : written; | ||
} | ||
|
||
long file_read_long(const char *path) | ||
{ | ||
char buf[128]; | ||
|
||
|
||
if (read_text(path, buf, sizeof(buf)) <= 0) | ||
return -1; | ||
|
||
return atol(buf); | ||
} | ||
|
||
int file_write_long(const char *path, long val) | ||
{ | ||
char buf[64]; | ||
int ret; | ||
|
||
ret = sprintf(buf, "%lu", val); | ||
if (ret < 0) | ||
return ret; | ||
|
||
if (write_text(path, buf, sizeof(buf)) <= 0) | ||
return -1; | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. | ||
* Copyright (c) 2024 Tejun Heo <[email protected]> | ||
*/ | ||
|
||
#ifndef __SCX_TEST_UTIL_H__ | ||
#define __SCX_TEST_UTIL_H__ | ||
|
||
long file_read_long(const char *path); | ||
int file_write_long(const char *path, long val); | ||
|
||
#endif // __SCX_TEST_H__ |