Skip to content
This repository has been archived by the owner on Jun 18, 2024. It is now read-only.

Commit

Permalink
scx: Add selftests for hotplug operations
Browse files Browse the repository at this point in the history
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
Byte-Lab committed Apr 10, 2024
1 parent 64b818f commit ef91247
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 1 deletion.
6 changes: 5 additions & 1 deletion tools/testing/selftests/sched_ext/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ auto-test-targets := \
ddsp_bogus_dsq_fail \
ddsp_vtimelocal_fail \
exit \
hotplug \
init_enable_count \
maximal \
maybe_null \
Expand Down Expand Up @@ -195,7 +196,10 @@ $(testcase-targets): $(SCXOBJ_DIR)/%.o: %.c $(SCXOBJ_DIR)/runner.o $(all_test_bp
$(eval test=$(patsubst %.o,%.c,$(notdir $@)))
$(CC) $(CFLAGS) -c $< -o $@ $(SCXOBJ_DIR)/runner.o

runner: $(SCXOBJ_DIR)/runner.o $(BPFOBJ) $(testcase-targets)
$(SCXOBJ_DIR)/util.o: util.c | $(SCXOBJ_DIR)
$(CC) $(CFLAGS) -c $< -o $@

runner: $(SCXOBJ_DIR)/runner.o $(SCXOBJ_DIR)/util.o $(BPFOBJ) $(testcase-targets)
@echo "$(testcase-targets)"
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

Expand Down
56 changes: 56 additions & 0 deletions tools/testing/selftests/sched_ext/hotplug.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* 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"

const volatile int hotplug_point;
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,
};
134 changes: 134 additions & 0 deletions tools/testing/selftests/sched_ext/hotplug.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* 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 "minimal_uei.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)
15 changes: 15 additions & 0 deletions tools/testing/selftests/sched_ext/hotplug_test.h
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__
27 changes: 27 additions & 0 deletions tools/testing/selftests/sched_ext/minimal_uei.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* A completely hotplug_restart scheduler.
*
* This scheduler defines a minimal scheduler that defines a UEI, but nothing
* else.
*
* 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";

UEI_DEFINE(uei);

void BPF_STRUCT_OPS(minimal_uei_exit, struct scx_exit_info *ei)
{
UEI_RECORD(uei, ei);
}

SEC(".struct_ops.link")
struct sched_ext_ops minimal_uei_ops = {
.exit = minimal_uei_exit,
.name = "minimal_uei",
};
18 changes: 18 additions & 0 deletions tools/testing/selftests/sched_ext/scx_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,22 @@ void scx_test_register(struct scx_test *test);
#define SCX_ASSERT(_x) SCX_FAIL_IF(!(_x), "Expected %s to be true (%lu)", \
#_x, (u64)(_x))

#define SCX_ECODE_VAL(__ecode) ({ \
u64 __val = 0; \
bool __found = false; \
\
__found = __COMPAT_read_enum("scx_exit_code", #__ecode, &__val); \
SCX_ASSERT(__found); \
(s64)__val; \
})

#define SCX_KIND_VAL(__kind) ({ \
u64 __val = 0; \
bool __found = false; \
\
__found = __COMPAT_read_enum("scx_exit_kind", #__kind, &__val); \
SCX_ASSERT(__found); \
__val; \
})

#endif // # __SCX_TEST_H__
71 changes: 71 additions & 0 deletions tools/testing/selftests/sched_ext/util.c
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;
}
13 changes: 13 additions & 0 deletions tools/testing/selftests/sched_ext/util.h
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__

0 comments on commit ef91247

Please sign in to comment.