From 26de71ec514d9b76203b9e4a9d5150c1937c3d65 Mon Sep 17 00:00:00 2001 From: David Vernet Date: Sun, 28 Jan 2024 14:48:21 -0600 Subject: [PATCH] scx: Add stress-test for loading schedulers in a tight loop In https://github.com/sched-ext/sched_ext/pull/129, Andrea fixed a bug where we trip over a NULL pointer deref by trying to load multiple schedulers at a time. Let's add a stress test that tries to load multiple schedulers in a tight loop at the same time. Additionally, do a bit of cleanup in the build system to have testcases take all BPF progs as dependencies. We don't really gain anything by artificially coupling the name of testcases to the BPF progs they use. Signed-off-by: David Vernet --- tools/testing/selftests/scx/Makefile | 16 ++--- tools/testing/selftests/scx/reload_loop.c | 76 +++++++++++++++++++++++ 2 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 tools/testing/selftests/scx/reload_loop.c diff --git a/tools/testing/selftests/scx/Makefile b/tools/testing/selftests/scx/Makefile index 4e8e5fe525ef6..c8d5231af31d3 100644 --- a/tools/testing/selftests/scx/Makefile +++ b/tools/testing/selftests/scx/Makefile @@ -155,22 +155,28 @@ override define CLEAN 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 := \ enq_last_no_enq_fails \ enq_select_cpu_fails \ ddsp_bogus_dsq_fail \ ddsp_vtimelocal_fail \ init_enable_count \ - maybe_null \ maximal \ + maybe_null \ minimal \ + 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 + test_example \ testcase-targets := $(addsuffix .o,$(addprefix $(SCXOBJ_DIR)/,$(auto-test-targets))) @@ -183,11 +189,7 @@ $(SCXOBJ_DIR)/runner.o: runner.c | $(SCXOBJ_DIR) # 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. -.SECONDEXPANSION: -$(testcase-targets): $(SCXOBJ_DIR)/%.o: %.c $(SCXOBJ_DIR)/runner.o \ - $$(if $$(wildcard $$*.bpf.c), $(INCLUDE_DIR)/%.bpf.skel.h) \ - $$(if $$(wildcard $$*_fail.bpf.c), $(INCLUDE_DIR)/%_fail.bpf.skel.h) \ - | $(SCXOBJ_DIR) +$(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 diff --git a/tools/testing/selftests/scx/reload_loop.c b/tools/testing/selftests/scx/reload_loop.c new file mode 100644 index 0000000000000..e64c5a8c5e198 --- /dev/null +++ b/tools/testing/selftests/scx/reload_loop.c @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Meta Platforms, Inc. and affiliates. + * Copyright (c) 2023 David Vernet + * Copyright (c) 2023 Tejun Heo + */ +#include +#include +#include +#include +#include +#include "maximal.bpf.skel.h" +#include "scx_test.h" + +static struct maximal *skel; +static pthread_t threads[2]; + +bool force_exit = false; + +static enum scx_test_status setup(void **ctx) +{ + skel = maximal__open_and_load(); + if (!skel) { + SCX_ERR("Failed to open and load skel"); + return SCX_TEST_FAIL; + } + + return SCX_TEST_PASS; +} + +static void *do_reload_loop(void *arg) +{ + u32 i; + + for (i = 0; i < 512 && !force_exit; i++) { + struct bpf_link *link; + + link = bpf_map__attach_struct_ops(skel->maps.maximal_ops); + if (link) + bpf_link__destroy(link); + } + + return NULL; +} + +static enum scx_test_status run(void *ctx) +{ + int err; + void *ret; + + err = pthread_create(&threads[0], NULL, do_reload_loop, NULL); + SCX_FAIL_IF(err, "Failed to create thread 0"); + + err = pthread_create(&threads[1], NULL, do_reload_loop, NULL); + SCX_FAIL_IF(err, "Failed to create thread 1"); + + SCX_FAIL_IF(pthread_join(threads[0], &ret), "thread 0 failed"); + SCX_FAIL_IF(pthread_join(threads[1], &ret), "thread 1 failed"); + + return SCX_TEST_PASS; +} + +static void cleanup(void *ctx) +{ + force_exit = true; + maximal__destroy(skel); +} + +struct scx_test reload_loop = { + .name = "reload_loop", + .description = "Stress test loading and unloading schedulers repeatedly in a tight loop", + .setup = setup, + .run = run, + .cleanup = cleanup, +}; +REGISTER_SCX_TEST(&reload_loop)