mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
sched_ext: Add selftests
Add basic selftests. Signed-off-by: David Vernet <dvernet@meta.com> Acked-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
*
|
||||
!*.c
|
||||
!*.h
|
||||
!Makefile
|
||||
!.gitignore
|
||||
!config
|
||||
@@ -0,0 +1,218 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
|
||||
include ../../../build/Build.include
|
||||
include ../../../scripts/Makefile.arch
|
||||
include ../../../scripts/Makefile.include
|
||||
include ../lib.mk
|
||||
|
||||
ifneq ($(LLVM),)
|
||||
ifneq ($(filter %/,$(LLVM)),)
|
||||
LLVM_PREFIX := $(LLVM)
|
||||
else ifneq ($(filter -%,$(LLVM)),)
|
||||
LLVM_SUFFIX := $(LLVM)
|
||||
endif
|
||||
|
||||
CC := $(LLVM_PREFIX)clang$(LLVM_SUFFIX) $(CLANG_FLAGS) -fintegrated-as
|
||||
else
|
||||
CC := gcc
|
||||
endif # LLVM
|
||||
|
||||
ifneq ($(CROSS_COMPILE),)
|
||||
$(error CROSS_COMPILE not supported for scx selftests)
|
||||
endif # CROSS_COMPILE
|
||||
|
||||
CURDIR := $(abspath .)
|
||||
REPOROOT := $(abspath ../../../..)
|
||||
TOOLSDIR := $(REPOROOT)/tools
|
||||
LIBDIR := $(TOOLSDIR)/lib
|
||||
BPFDIR := $(LIBDIR)/bpf
|
||||
TOOLSINCDIR := $(TOOLSDIR)/include
|
||||
BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool
|
||||
APIDIR := $(TOOLSINCDIR)/uapi
|
||||
GENDIR := $(REPOROOT)/include/generated
|
||||
GENHDR := $(GENDIR)/autoconf.h
|
||||
SCXTOOLSDIR := $(TOOLSDIR)/sched_ext
|
||||
SCXTOOLSINCDIR := $(TOOLSDIR)/sched_ext/include
|
||||
|
||||
OUTPUT_DIR := $(CURDIR)/build
|
||||
OBJ_DIR := $(OUTPUT_DIR)/obj
|
||||
INCLUDE_DIR := $(OUTPUT_DIR)/include
|
||||
BPFOBJ_DIR := $(OBJ_DIR)/libbpf
|
||||
SCXOBJ_DIR := $(OBJ_DIR)/sched_ext
|
||||
BPFOBJ := $(BPFOBJ_DIR)/libbpf.a
|
||||
LIBBPF_OUTPUT := $(OBJ_DIR)/libbpf/libbpf.a
|
||||
DEFAULT_BPFTOOL := $(OUTPUT_DIR)/sbin/bpftool
|
||||
HOST_BUILD_DIR := $(OBJ_DIR)
|
||||
HOST_OUTPUT_DIR := $(OUTPUT_DIR)
|
||||
|
||||
VMLINUX_BTF_PATHS ?= ../../../../vmlinux \
|
||||
/sys/kernel/btf/vmlinux \
|
||||
/boot/vmlinux-$(shell uname -r)
|
||||
VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
|
||||
ifeq ($(VMLINUX_BTF),)
|
||||
$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
|
||||
endif
|
||||
|
||||
BPFTOOL ?= $(DEFAULT_BPFTOOL)
|
||||
|
||||
ifneq ($(wildcard $(GENHDR)),)
|
||||
GENFLAGS := -DHAVE_GENHDR
|
||||
endif
|
||||
|
||||
CFLAGS += -g -O2 -rdynamic -pthread -Wall -Werror $(GENFLAGS) \
|
||||
-I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR) \
|
||||
-I$(TOOLSINCDIR) -I$(APIDIR) -I$(CURDIR)/include -I$(SCXTOOLSINCDIR)
|
||||
|
||||
# Silence some warnings when compiled with clang
|
||||
ifneq ($(LLVM),)
|
||||
CFLAGS += -Wno-unused-command-line-argument
|
||||
endif
|
||||
|
||||
LDFLAGS = -lelf -lz -lpthread -lzstd
|
||||
|
||||
IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - </dev/null | \
|
||||
grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
|
||||
|
||||
# Get Clang's default includes on this system, as opposed to those seen by
|
||||
# '-target bpf'. This fixes "missing" files on some architectures/distros,
|
||||
# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc.
|
||||
#
|
||||
# Use '-idirafter': Don't interfere with include mechanics except where the
|
||||
# build would have failed anyways.
|
||||
define get_sys_includes
|
||||
$(shell $(1) -v -E - </dev/null 2>&1 \
|
||||
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \
|
||||
$(shell $(1) -dM -E - </dev/null | grep '__riscv_xlen ' | awk '{printf("-D__riscv_xlen=%d -D__BITS_PER_LONG=%d", $$3, $$3)}')
|
||||
endef
|
||||
|
||||
BPF_CFLAGS = -g -D__TARGET_ARCH_$(SRCARCH) \
|
||||
$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian) \
|
||||
-I$(CURDIR)/include -I$(CURDIR)/include/bpf-compat \
|
||||
-I$(INCLUDE_DIR) -I$(APIDIR) -I$(SCXTOOLSINCDIR) \
|
||||
-I$(REPOROOT)/include \
|
||||
$(call get_sys_includes,$(CLANG)) \
|
||||
-Wall -Wno-compare-distinct-pointer-types \
|
||||
-Wno-incompatible-function-pointer-types \
|
||||
-O2 -mcpu=v3
|
||||
|
||||
# sort removes libbpf duplicates when not cross-building
|
||||
MAKE_DIRS := $(sort $(OBJ_DIR)/libbpf $(OBJ_DIR)/libbpf \
|
||||
$(OBJ_DIR)/bpftool $(OBJ_DIR)/resolve_btfids \
|
||||
$(INCLUDE_DIR) $(SCXOBJ_DIR))
|
||||
|
||||
$(MAKE_DIRS):
|
||||
$(call msg,MKDIR,,$@)
|
||||
$(Q)mkdir -p $@
|
||||
|
||||
$(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile) \
|
||||
$(APIDIR)/linux/bpf.h \
|
||||
| $(OBJ_DIR)/libbpf
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(BPFDIR) OUTPUT=$(OBJ_DIR)/libbpf/ \
|
||||
EXTRA_CFLAGS='-g -O0 -fPIC' \
|
||||
DESTDIR=$(OUTPUT_DIR) prefix= all install_headers
|
||||
|
||||
$(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \
|
||||
$(LIBBPF_OUTPUT) | $(OBJ_DIR)/bpftool
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(BPFTOOLDIR) \
|
||||
ARCH= CROSS_COMPILE= CC=$(HOSTCC) LD=$(HOSTLD) \
|
||||
EXTRA_CFLAGS='-g -O0' \
|
||||
OUTPUT=$(OBJ_DIR)/bpftool/ \
|
||||
LIBBPF_OUTPUT=$(OBJ_DIR)/libbpf/ \
|
||||
LIBBPF_DESTDIR=$(OUTPUT_DIR)/ \
|
||||
prefix= DESTDIR=$(OUTPUT_DIR)/ install-bin
|
||||
|
||||
$(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR)
|
||||
ifeq ($(VMLINUX_H),)
|
||||
$(call msg,GEN,,$@)
|
||||
$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
|
||||
else
|
||||
$(call msg,CP,,$@)
|
||||
$(Q)cp "$(VMLINUX_H)" $@
|
||||
endif
|
||||
|
||||
$(SCXOBJ_DIR)/%.bpf.o: %.bpf.c $(INCLUDE_DIR)/vmlinux.h | $(BPFOBJ) $(SCXOBJ_DIR)
|
||||
$(call msg,CLNG-BPF,,$(notdir $@))
|
||||
$(Q)$(CLANG) $(BPF_CFLAGS) -target bpf -c $< -o $@
|
||||
|
||||
$(INCLUDE_DIR)/%.bpf.skel.h: $(SCXOBJ_DIR)/%.bpf.o $(INCLUDE_DIR)/vmlinux.h $(BPFTOOL) | $(INCLUDE_DIR)
|
||||
$(eval sched=$(notdir $@))
|
||||
$(call msg,GEN-SKEL,,$(sched))
|
||||
$(Q)$(BPFTOOL) gen object $(<:.o=.linked1.o) $<
|
||||
$(Q)$(BPFTOOL) gen object $(<:.o=.linked2.o) $(<:.o=.linked1.o)
|
||||
$(Q)$(BPFTOOL) gen object $(<:.o=.linked3.o) $(<:.o=.linked2.o)
|
||||
$(Q)diff $(<:.o=.linked2.o) $(<:.o=.linked3.o)
|
||||
$(Q)$(BPFTOOL) gen skeleton $(<:.o=.linked3.o) name $(subst .bpf.skel.h,,$(sched)) > $@
|
||||
$(Q)$(BPFTOOL) gen subskeleton $(<:.o=.linked3.o) name $(subst .bpf.skel.h,,$(sched)) > $(@:.skel.h=.subskel.h)
|
||||
|
||||
################
|
||||
# C schedulers #
|
||||
################
|
||||
|
||||
override define CLEAN
|
||||
rm -rf $(OUTPUT_DIR)
|
||||
rm -f *.o *.bpf.o *.bpf.skel.h *.bpf.subskel.h
|
||||
rm -f $(TEST_GEN_PROGS)
|
||||
rm -f runner
|
||||
endef
|
||||
|
||||
# Every testcase takes all of the BPF progs are dependencies by default. This
|
||||
# allows testcases to load any BPF scheduler, which is useful for testcases
|
||||
# that don't need their own prog to run their test.
|
||||
all_test_bpfprogs := $(foreach prog,$(wildcard *.bpf.c),$(INCLUDE_DIR)/$(patsubst %.c,%.skel.h,$(prog)))
|
||||
|
||||
auto-test-targets := \
|
||||
create_dsq \
|
||||
enq_last_no_enq_fails \
|
||||
enq_select_cpu_fails \
|
||||
ddsp_bogus_dsq_fail \
|
||||
ddsp_vtimelocal_fail \
|
||||
dsp_local_on \
|
||||
exit \
|
||||
hotplug \
|
||||
init_enable_count \
|
||||
maximal \
|
||||
maybe_null \
|
||||
minimal \
|
||||
prog_run \
|
||||
reload_loop \
|
||||
select_cpu_dfl \
|
||||
select_cpu_dfl_nodispatch \
|
||||
select_cpu_dispatch \
|
||||
select_cpu_dispatch_bad_dsq \
|
||||
select_cpu_dispatch_dbl_dsp \
|
||||
select_cpu_vtime \
|
||||
test_example \
|
||||
|
||||
testcase-targets := $(addsuffix .o,$(addprefix $(SCXOBJ_DIR)/,$(auto-test-targets)))
|
||||
|
||||
$(SCXOBJ_DIR)/runner.o: runner.c | $(SCXOBJ_DIR)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Create all of the test targets object files, whose testcase objects will be
|
||||
# registered into the runner in ELF constructors.
|
||||
#
|
||||
# Note that we must do double expansion here in order to support conditionally
|
||||
# compiling BPF object files only if one is present, as the wildcard Make
|
||||
# function doesn't support using implicit rules otherwise.
|
||||
$(testcase-targets): $(SCXOBJ_DIR)/%.o: %.c $(SCXOBJ_DIR)/runner.o $(all_test_bpfprogs) | $(SCXOBJ_DIR)
|
||||
$(eval test=$(patsubst %.o,%.c,$(notdir $@)))
|
||||
$(CC) $(CFLAGS) -c $< -o $@ $(SCXOBJ_DIR)/runner.o
|
||||
|
||||
$(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)
|
||||
|
||||
TEST_GEN_PROGS := runner
|
||||
|
||||
all: runner
|
||||
|
||||
.PHONY: all clean help
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
.SECONDARY:
|
||||
@@ -0,0 +1,9 @@
|
||||
CONFIG_SCHED_DEBUG=y
|
||||
CONFIG_SCHED_CLASS_EXT=y
|
||||
CONFIG_CGROUPS=y
|
||||
CONFIG_CGROUP_SCHED=y
|
||||
CONFIG_EXT_GROUP_SCHED=y
|
||||
CONFIG_BPF=y
|
||||
CONFIG_BPF_SYSCALL=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_INFO_BTF=y
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Create and destroy DSQs in a loop.
|
||||
*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
*/
|
||||
|
||||
#include <scx/common.bpf.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
void BPF_STRUCT_OPS(create_dsq_exit_task, struct task_struct *p,
|
||||
struct scx_exit_task_args *args)
|
||||
{
|
||||
scx_bpf_destroy_dsq(p->pid);
|
||||
}
|
||||
|
||||
s32 BPF_STRUCT_OPS_SLEEPABLE(create_dsq_init_task, struct task_struct *p,
|
||||
struct scx_init_task_args *args)
|
||||
{
|
||||
s32 err;
|
||||
|
||||
err = scx_bpf_create_dsq(p->pid, -1);
|
||||
if (err)
|
||||
scx_bpf_error("Failed to create DSQ for %s[%d]",
|
||||
p->comm, p->pid);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
s32 BPF_STRUCT_OPS_SLEEPABLE(create_dsq_init)
|
||||
{
|
||||
u32 i;
|
||||
s32 err;
|
||||
|
||||
bpf_for(i, 0, 1024) {
|
||||
err = scx_bpf_create_dsq(i, -1);
|
||||
if (err) {
|
||||
scx_bpf_error("Failed to create DSQ %d", i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bpf_for(i, 0, 1024) {
|
||||
scx_bpf_destroy_dsq(i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct sched_ext_ops create_dsq_ops = {
|
||||
.init_task = create_dsq_init_task,
|
||||
.exit_task = create_dsq_exit_task,
|
||||
.init = create_dsq_init,
|
||||
.name = "create_dsq",
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
/* 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 <scx/common.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "create_dsq.bpf.skel.h"
|
||||
#include "scx_test.h"
|
||||
|
||||
static enum scx_test_status setup(void **ctx)
|
||||
{
|
||||
struct create_dsq *skel;
|
||||
|
||||
skel = create_dsq__open_and_load();
|
||||
if (!skel) {
|
||||
SCX_ERR("Failed to open and load skel");
|
||||
return SCX_TEST_FAIL;
|
||||
}
|
||||
*ctx = skel;
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static enum scx_test_status run(void *ctx)
|
||||
{
|
||||
struct create_dsq *skel = ctx;
|
||||
struct bpf_link *link;
|
||||
|
||||
link = bpf_map__attach_struct_ops(skel->maps.create_dsq_ops);
|
||||
if (!link) {
|
||||
SCX_ERR("Failed to attach scheduler");
|
||||
return SCX_TEST_FAIL;
|
||||
}
|
||||
|
||||
bpf_link__destroy(link);
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static void cleanup(void *ctx)
|
||||
{
|
||||
struct create_dsq *skel = ctx;
|
||||
|
||||
create_dsq__destroy(skel);
|
||||
}
|
||||
|
||||
struct scx_test create_dsq = {
|
||||
.name = "create_dsq",
|
||||
.description = "Create and destroy a dsq in a loop",
|
||||
.setup = setup,
|
||||
.run = run,
|
||||
.cleanup = cleanup,
|
||||
};
|
||||
REGISTER_SCX_TEST(&create_dsq)
|
||||
@@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
* Copyright (c) 2024 Tejun Heo <tj@kernel.org>
|
||||
*/
|
||||
#include <scx/common.bpf.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
UEI_DEFINE(uei);
|
||||
|
||||
s32 BPF_STRUCT_OPS(ddsp_bogus_dsq_fail_select_cpu, struct task_struct *p,
|
||||
s32 prev_cpu, u64 wake_flags)
|
||||
{
|
||||
s32 cpu = scx_bpf_pick_idle_cpu(p->cpus_ptr, 0);
|
||||
|
||||
if (cpu >= 0) {
|
||||
/*
|
||||
* If we dispatch to a bogus DSQ that will fall back to the
|
||||
* builtin global DSQ, we fail gracefully.
|
||||
*/
|
||||
scx_bpf_dispatch_vtime(p, 0xcafef00d, SCX_SLICE_DFL,
|
||||
p->scx.dsq_vtime, 0);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
return prev_cpu;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(ddsp_bogus_dsq_fail_exit, struct scx_exit_info *ei)
|
||||
{
|
||||
UEI_RECORD(uei, ei);
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct sched_ext_ops ddsp_bogus_dsq_fail_ops = {
|
||||
.select_cpu = ddsp_bogus_dsq_fail_select_cpu,
|
||||
.exit = ddsp_bogus_dsq_fail_exit,
|
||||
.name = "ddsp_bogus_dsq_fail",
|
||||
.timeout_ms = 1000U,
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
* Copyright (c) 2024 Tejun Heo <tj@kernel.org>
|
||||
*/
|
||||
#include <bpf/bpf.h>
|
||||
#include <scx/common.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "ddsp_bogus_dsq_fail.bpf.skel.h"
|
||||
#include "scx_test.h"
|
||||
|
||||
static enum scx_test_status setup(void **ctx)
|
||||
{
|
||||
struct ddsp_bogus_dsq_fail *skel;
|
||||
|
||||
skel = ddsp_bogus_dsq_fail__open_and_load();
|
||||
SCX_FAIL_IF(!skel, "Failed to open and load skel");
|
||||
*ctx = skel;
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static enum scx_test_status run(void *ctx)
|
||||
{
|
||||
struct ddsp_bogus_dsq_fail *skel = ctx;
|
||||
struct bpf_link *link;
|
||||
|
||||
link = bpf_map__attach_struct_ops(skel->maps.ddsp_bogus_dsq_fail_ops);
|
||||
SCX_FAIL_IF(!link, "Failed to attach struct_ops");
|
||||
|
||||
sleep(1);
|
||||
|
||||
SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_ERROR));
|
||||
bpf_link__destroy(link);
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static void cleanup(void *ctx)
|
||||
{
|
||||
struct ddsp_bogus_dsq_fail *skel = ctx;
|
||||
|
||||
ddsp_bogus_dsq_fail__destroy(skel);
|
||||
}
|
||||
|
||||
struct scx_test ddsp_bogus_dsq_fail = {
|
||||
.name = "ddsp_bogus_dsq_fail",
|
||||
.description = "Verify we gracefully fail, and fall back to using a "
|
||||
"built-in DSQ, if we do a direct dispatch to an invalid"
|
||||
" DSQ in ops.select_cpu()",
|
||||
.setup = setup,
|
||||
.run = run,
|
||||
.cleanup = cleanup,
|
||||
};
|
||||
REGISTER_SCX_TEST(&ddsp_bogus_dsq_fail)
|
||||
@@ -0,0 +1,39 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
* Copyright (c) 2024 Tejun Heo <tj@kernel.org>
|
||||
*/
|
||||
#include <scx/common.bpf.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
UEI_DEFINE(uei);
|
||||
|
||||
s32 BPF_STRUCT_OPS(ddsp_vtimelocal_fail_select_cpu, struct task_struct *p,
|
||||
s32 prev_cpu, u64 wake_flags)
|
||||
{
|
||||
s32 cpu = scx_bpf_pick_idle_cpu(p->cpus_ptr, 0);
|
||||
|
||||
if (cpu >= 0) {
|
||||
/* Shouldn't be allowed to vtime dispatch to a builtin DSQ. */
|
||||
scx_bpf_dispatch_vtime(p, SCX_DSQ_LOCAL, SCX_SLICE_DFL,
|
||||
p->scx.dsq_vtime, 0);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
return prev_cpu;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(ddsp_vtimelocal_fail_exit, struct scx_exit_info *ei)
|
||||
{
|
||||
UEI_RECORD(uei, ei);
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct sched_ext_ops ddsp_vtimelocal_fail_ops = {
|
||||
.select_cpu = ddsp_vtimelocal_fail_select_cpu,
|
||||
.exit = ddsp_vtimelocal_fail_exit,
|
||||
.name = "ddsp_vtimelocal_fail",
|
||||
.timeout_ms = 1000U,
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
* Copyright (c) 2024 Tejun Heo <tj@kernel.org>
|
||||
*/
|
||||
#include <bpf/bpf.h>
|
||||
#include <scx/common.h>
|
||||
#include <unistd.h>
|
||||
#include "ddsp_vtimelocal_fail.bpf.skel.h"
|
||||
#include "scx_test.h"
|
||||
|
||||
static enum scx_test_status setup(void **ctx)
|
||||
{
|
||||
struct ddsp_vtimelocal_fail *skel;
|
||||
|
||||
skel = ddsp_vtimelocal_fail__open_and_load();
|
||||
SCX_FAIL_IF(!skel, "Failed to open and load skel");
|
||||
*ctx = skel;
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static enum scx_test_status run(void *ctx)
|
||||
{
|
||||
struct ddsp_vtimelocal_fail *skel = ctx;
|
||||
struct bpf_link *link;
|
||||
|
||||
link = bpf_map__attach_struct_ops(skel->maps.ddsp_vtimelocal_fail_ops);
|
||||
SCX_FAIL_IF(!link, "Failed to attach struct_ops");
|
||||
|
||||
sleep(1);
|
||||
|
||||
SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_ERROR));
|
||||
bpf_link__destroy(link);
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static void cleanup(void *ctx)
|
||||
{
|
||||
struct ddsp_vtimelocal_fail *skel = ctx;
|
||||
|
||||
ddsp_vtimelocal_fail__destroy(skel);
|
||||
}
|
||||
|
||||
struct scx_test ddsp_vtimelocal_fail = {
|
||||
.name = "ddsp_vtimelocal_fail",
|
||||
.description = "Verify we gracefully fail, and fall back to using a "
|
||||
"built-in DSQ, if we do a direct vtime dispatch to a "
|
||||
"built-in DSQ from DSQ in ops.select_cpu()",
|
||||
.setup = setup,
|
||||
.run = run,
|
||||
.cleanup = cleanup,
|
||||
};
|
||||
REGISTER_SCX_TEST(&ddsp_vtimelocal_fail)
|
||||
@@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
*/
|
||||
#include <scx/common.bpf.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
const volatile s32 nr_cpus;
|
||||
|
||||
UEI_DEFINE(uei);
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_QUEUE);
|
||||
__uint(max_entries, 8192);
|
||||
__type(value, s32);
|
||||
} queue SEC(".maps");
|
||||
|
||||
s32 BPF_STRUCT_OPS(dsp_local_on_select_cpu, struct task_struct *p,
|
||||
s32 prev_cpu, u64 wake_flags)
|
||||
{
|
||||
return prev_cpu;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(dsp_local_on_enqueue, struct task_struct *p,
|
||||
u64 enq_flags)
|
||||
{
|
||||
s32 pid = p->pid;
|
||||
|
||||
if (bpf_map_push_elem(&queue, &pid, 0))
|
||||
scx_bpf_error("Failed to enqueue %s[%d]", p->comm, p->pid);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(dsp_local_on_dispatch, s32 cpu, struct task_struct *prev)
|
||||
{
|
||||
s32 pid, target;
|
||||
struct task_struct *p;
|
||||
|
||||
if (bpf_map_pop_elem(&queue, &pid))
|
||||
return;
|
||||
|
||||
p = bpf_task_from_pid(pid);
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
target = bpf_get_prandom_u32() % nr_cpus;
|
||||
|
||||
scx_bpf_dispatch(p, SCX_DSQ_LOCAL_ON | target, SCX_SLICE_DFL, 0);
|
||||
bpf_task_release(p);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(dsp_local_on_exit, struct scx_exit_info *ei)
|
||||
{
|
||||
UEI_RECORD(uei, ei);
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct sched_ext_ops dsp_local_on_ops = {
|
||||
.select_cpu = dsp_local_on_select_cpu,
|
||||
.enqueue = dsp_local_on_enqueue,
|
||||
.dispatch = dsp_local_on_dispatch,
|
||||
.exit = dsp_local_on_exit,
|
||||
.name = "dsp_local_on",
|
||||
.timeout_ms = 1000U,
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/* 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 <scx/common.h>
|
||||
#include <unistd.h>
|
||||
#include "dsp_local_on.bpf.skel.h"
|
||||
#include "scx_test.h"
|
||||
|
||||
static enum scx_test_status setup(void **ctx)
|
||||
{
|
||||
struct dsp_local_on *skel;
|
||||
|
||||
skel = dsp_local_on__open();
|
||||
SCX_FAIL_IF(!skel, "Failed to open");
|
||||
|
||||
skel->rodata->nr_cpus = libbpf_num_possible_cpus();
|
||||
SCX_FAIL_IF(dsp_local_on__load(skel), "Failed to load skel");
|
||||
*ctx = skel;
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static enum scx_test_status run(void *ctx)
|
||||
{
|
||||
struct dsp_local_on *skel = ctx;
|
||||
struct bpf_link *link;
|
||||
|
||||
link = bpf_map__attach_struct_ops(skel->maps.dsp_local_on_ops);
|
||||
SCX_FAIL_IF(!link, "Failed to attach struct_ops");
|
||||
|
||||
/* Just sleeping is fine, plenty of scheduling events happening */
|
||||
sleep(1);
|
||||
|
||||
SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_ERROR));
|
||||
bpf_link__destroy(link);
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static void cleanup(void *ctx)
|
||||
{
|
||||
struct dsp_local_on *skel = ctx;
|
||||
|
||||
dsp_local_on__destroy(skel);
|
||||
}
|
||||
|
||||
struct scx_test dsp_local_on = {
|
||||
.name = "dsp_local_on",
|
||||
.description = "Verify we can directly dispatch tasks to a local DSQs "
|
||||
"from osp.dispatch()",
|
||||
.setup = setup,
|
||||
.run = run,
|
||||
.cleanup = cleanup,
|
||||
};
|
||||
REGISTER_SCX_TEST(&dsp_local_on)
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* A scheduler that validates the behavior of direct dispatching with a default
|
||||
* select_cpu implementation.
|
||||
*
|
||||
* Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2023 David Vernet <dvernet@meta.com>
|
||||
* Copyright (c) 2023 Tejun Heo <tj@kernel.org>
|
||||
*/
|
||||
|
||||
#include <scx/common.bpf.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct sched_ext_ops enq_last_no_enq_fails_ops = {
|
||||
.name = "enq_last_no_enq_fails",
|
||||
/* Need to define ops.enqueue() with SCX_OPS_ENQ_LAST */
|
||||
.flags = SCX_OPS_ENQ_LAST,
|
||||
.timeout_ms = 1000U,
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2023 David Vernet <dvernet@meta.com>
|
||||
* Copyright (c) 2023 Tejun Heo <tj@kernel.org>
|
||||
*/
|
||||
#include <bpf/bpf.h>
|
||||
#include <scx/common.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "enq_last_no_enq_fails.bpf.skel.h"
|
||||
#include "scx_test.h"
|
||||
|
||||
static enum scx_test_status setup(void **ctx)
|
||||
{
|
||||
struct enq_last_no_enq_fails *skel;
|
||||
|
||||
skel = enq_last_no_enq_fails__open_and_load();
|
||||
if (!skel) {
|
||||
SCX_ERR("Failed to open and load skel");
|
||||
return SCX_TEST_FAIL;
|
||||
}
|
||||
*ctx = skel;
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static enum scx_test_status run(void *ctx)
|
||||
{
|
||||
struct enq_last_no_enq_fails *skel = ctx;
|
||||
struct bpf_link *link;
|
||||
|
||||
link = bpf_map__attach_struct_ops(skel->maps.enq_last_no_enq_fails_ops);
|
||||
if (link) {
|
||||
SCX_ERR("Incorrectly succeeded in to attaching scheduler");
|
||||
return SCX_TEST_FAIL;
|
||||
}
|
||||
|
||||
bpf_link__destroy(link);
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static void cleanup(void *ctx)
|
||||
{
|
||||
struct enq_last_no_enq_fails *skel = ctx;
|
||||
|
||||
enq_last_no_enq_fails__destroy(skel);
|
||||
}
|
||||
|
||||
struct scx_test enq_last_no_enq_fails = {
|
||||
.name = "enq_last_no_enq_fails",
|
||||
.description = "Verify we fail to load a scheduler if we specify "
|
||||
"the SCX_OPS_ENQ_LAST flag without defining "
|
||||
"ops.enqueue()",
|
||||
.setup = setup,
|
||||
.run = run,
|
||||
.cleanup = cleanup,
|
||||
};
|
||||
REGISTER_SCX_TEST(&enq_last_no_enq_fails)
|
||||
@@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2023 David Vernet <dvernet@meta.com>
|
||||
* Copyright (c) 2023 Tejun Heo <tj@kernel.org>
|
||||
*/
|
||||
|
||||
#include <scx/common.bpf.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
/* Manually specify the signature until the kfunc is added to the scx repo. */
|
||||
s32 scx_bpf_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags,
|
||||
bool *found) __ksym;
|
||||
|
||||
s32 BPF_STRUCT_OPS(enq_select_cpu_fails_select_cpu, struct task_struct *p,
|
||||
s32 prev_cpu, u64 wake_flags)
|
||||
{
|
||||
return prev_cpu;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(enq_select_cpu_fails_enqueue, struct task_struct *p,
|
||||
u64 enq_flags)
|
||||
{
|
||||
/*
|
||||
* Need to initialize the variable or the verifier will fail to load.
|
||||
* Improving these semantics is actively being worked on.
|
||||
*/
|
||||
bool found = false;
|
||||
|
||||
/* Can only call from ops.select_cpu() */
|
||||
scx_bpf_select_cpu_dfl(p, 0, 0, &found);
|
||||
|
||||
scx_bpf_dispatch(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct sched_ext_ops enq_select_cpu_fails_ops = {
|
||||
.select_cpu = enq_select_cpu_fails_select_cpu,
|
||||
.enqueue = enq_select_cpu_fails_enqueue,
|
||||
.name = "enq_select_cpu_fails",
|
||||
.timeout_ms = 1000U,
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2023 David Vernet <dvernet@meta.com>
|
||||
* Copyright (c) 2023 Tejun Heo <tj@kernel.org>
|
||||
*/
|
||||
#include <bpf/bpf.h>
|
||||
#include <scx/common.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include "enq_select_cpu_fails.bpf.skel.h"
|
||||
#include "scx_test.h"
|
||||
|
||||
static enum scx_test_status setup(void **ctx)
|
||||
{
|
||||
struct enq_select_cpu_fails *skel;
|
||||
|
||||
skel = enq_select_cpu_fails__open_and_load();
|
||||
if (!skel) {
|
||||
SCX_ERR("Failed to open and load skel");
|
||||
return SCX_TEST_FAIL;
|
||||
}
|
||||
*ctx = skel;
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static enum scx_test_status run(void *ctx)
|
||||
{
|
||||
struct enq_select_cpu_fails *skel = ctx;
|
||||
struct bpf_link *link;
|
||||
|
||||
link = bpf_map__attach_struct_ops(skel->maps.enq_select_cpu_fails_ops);
|
||||
if (!link) {
|
||||
SCX_ERR("Failed to attach scheduler");
|
||||
return SCX_TEST_FAIL;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
|
||||
bpf_link__destroy(link);
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
static void cleanup(void *ctx)
|
||||
{
|
||||
struct enq_select_cpu_fails *skel = ctx;
|
||||
|
||||
enq_select_cpu_fails__destroy(skel);
|
||||
}
|
||||
|
||||
struct scx_test enq_select_cpu_fails = {
|
||||
.name = "enq_select_cpu_fails",
|
||||
.description = "Verify we fail to call scx_bpf_select_cpu_dfl() "
|
||||
"from ops.enqueue()",
|
||||
.setup = setup,
|
||||
.run = run,
|
||||
.cleanup = cleanup,
|
||||
};
|
||||
REGISTER_SCX_TEST(&enq_select_cpu_fails)
|
||||
@@ -0,0 +1,84 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
*/
|
||||
|
||||
#include <scx/common.bpf.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
#include "exit_test.h"
|
||||
|
||||
const volatile int exit_point;
|
||||
UEI_DEFINE(uei);
|
||||
|
||||
#define EXIT_CLEANLY() scx_bpf_exit(exit_point, "%d", exit_point)
|
||||
|
||||
s32 BPF_STRUCT_OPS(exit_select_cpu, struct task_struct *p,
|
||||
s32 prev_cpu, u64 wake_flags)
|
||||
{
|
||||
bool found;
|
||||
|
||||
if (exit_point == EXIT_SELECT_CPU)
|
||||
EXIT_CLEANLY();
|
||||
|
||||
return scx_bpf_select_cpu_dfl(p, prev_cpu, wake_flags, &found);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(exit_enqueue, struct task_struct *p, u64 enq_flags)
|
||||
{
|
||||
if (exit_point == EXIT_ENQUEUE)
|
||||
EXIT_CLEANLY();
|
||||
|
||||
scx_bpf_dispatch(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(exit_dispatch, s32 cpu, struct task_struct *p)
|
||||
{
|
||||
if (exit_point == EXIT_DISPATCH)
|
||||
EXIT_CLEANLY();
|
||||
|
||||
scx_bpf_consume(SCX_DSQ_GLOBAL);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(exit_enable, struct task_struct *p)
|
||||
{
|
||||
if (exit_point == EXIT_ENABLE)
|
||||
EXIT_CLEANLY();
|
||||
}
|
||||
|
||||
s32 BPF_STRUCT_OPS(exit_init_task, struct task_struct *p,
|
||||
struct scx_init_task_args *args)
|
||||
{
|
||||
if (exit_point == EXIT_INIT_TASK)
|
||||
EXIT_CLEANLY();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(exit_exit, struct scx_exit_info *ei)
|
||||
{
|
||||
UEI_RECORD(uei, ei);
|
||||
}
|
||||
|
||||
s32 BPF_STRUCT_OPS_SLEEPABLE(exit_init)
|
||||
{
|
||||
if (exit_point == EXIT_INIT)
|
||||
EXIT_CLEANLY();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct sched_ext_ops exit_ops = {
|
||||
.select_cpu = exit_select_cpu,
|
||||
.enqueue = exit_enqueue,
|
||||
.dispatch = exit_dispatch,
|
||||
.init_task = exit_init_task,
|
||||
.enable = exit_enable,
|
||||
.exit = exit_exit,
|
||||
.init = exit_init,
|
||||
.name = "exit",
|
||||
.timeout_ms = 1000U,
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/* 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 "exit.bpf.skel.h"
|
||||
#include "scx_test.h"
|
||||
|
||||
#include "exit_test.h"
|
||||
|
||||
static enum scx_test_status run(void *ctx)
|
||||
{
|
||||
enum exit_test_case tc;
|
||||
|
||||
for (tc = 0; tc < NUM_EXITS; tc++) {
|
||||
struct exit *skel;
|
||||
struct bpf_link *link;
|
||||
char buf[16];
|
||||
|
||||
skel = exit__open();
|
||||
skel->rodata->exit_point = tc;
|
||||
exit__load(skel);
|
||||
link = bpf_map__attach_struct_ops(skel->maps.exit_ops);
|
||||
if (!link) {
|
||||
SCX_ERR("Failed to attach scheduler");
|
||||
exit__destroy(skel);
|
||||
return SCX_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* Assumes uei.kind is written last */
|
||||
while (skel->data->uei.kind == EXIT_KIND(SCX_EXIT_NONE))
|
||||
sched_yield();
|
||||
|
||||
SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_UNREG_BPF));
|
||||
SCX_EQ(skel->data->uei.exit_code, tc);
|
||||
sprintf(buf, "%d", tc);
|
||||
SCX_ASSERT(!strcmp(skel->data->uei.msg, buf));
|
||||
bpf_link__destroy(link);
|
||||
exit__destroy(skel);
|
||||
}
|
||||
|
||||
return SCX_TEST_PASS;
|
||||
}
|
||||
|
||||
struct scx_test exit_test = {
|
||||
.name = "exit",
|
||||
.description = "Verify we can cleanly exit a scheduler in multiple places",
|
||||
.run = run,
|
||||
};
|
||||
REGISTER_SCX_TEST(&exit_test)
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
*/
|
||||
|
||||
#ifndef __EXIT_TEST_H__
|
||||
#define __EXIT_TEST_H__
|
||||
|
||||
enum exit_test_case {
|
||||
EXIT_SELECT_CPU,
|
||||
EXIT_ENQUEUE,
|
||||
EXIT_DISPATCH,
|
||||
EXIT_ENABLE,
|
||||
EXIT_INIT_TASK,
|
||||
EXIT_INIT,
|
||||
NUM_EXITS,
|
||||
};
|
||||
|
||||
#endif // # __EXIT_TEST_H__
|
||||
@@ -0,0 +1,61 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
|
||||
* Copyright (c) 2024 David Vernet <dvernet@meta.com>
|
||||
*/
|
||||
|
||||
#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)
|
||||
{
|
||||
/*
|
||||
* Ignored, just used to verify that we can invoke blocking kfuncs
|
||||
* from the hotplug path.
|
||||
*/
|
||||
scx_bpf_create_dsq(0, -1);
|
||||
|
||||
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_SLEEPABLE(hotplug_cpu_online, s32 cpu)
|
||||
{
|
||||
exit_from_hotplug(cpu, true);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS_SLEEPABLE(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,
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
/* 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 <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"
|
||||
|
||||
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_and_load();
|
||||
SCX_ASSERT(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)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user