mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 1/2] selftests/sched_ext: Add basic sub-sched functionality test
@ 2026-03-23  6:18 zhidao su
  2026-03-23  6:18 ` [PATCH 2/2] selftests/sched_ext: Add failure-path tests for sub-sched zhidao su
  0 siblings, 1 reply; 3+ messages in thread
From: zhidao su @ 2026-03-23  6:18 UTC (permalink / raw)
  To: sched-ext
  Cc: linux-kernel, linux-kselftest, tj, void, arighi, changwoo,
	peterz, mingo, shuah, zhidao su

Implement a new selftest to verify sub-sched (nested scheduler)
attachment and detachment. This test was missing despite sub-sched
being a complex feature that has seen multiple bug fixes (cgroup
double-put, uninitialized return value, abort path issues).

Sub-sched allows a child BPF scheduler to manage tasks within a
specific cgroup subtree, coordinating with the parent scheduler
via sub_attach/sub_detach operations.

Test structure:
- Parent BPF scheduler: Implements sub_attach/sub_detach ops;
  sub_attach returns s32 as required by the kernel ABI
- Child BPF scheduler: Minimal dispatch logic, configured with
  sub_cgroup_id pointing to a test cgroup
- Test driver: Creates a cgroup under /sys/fs/cgroup/ (cgroupfs),
  obtains the cgroup ID via stat().st_ino (which equals the kernfs
  node ID on cgroupfs), loads both schedulers, attaches child to
  the cgroup, verifies clean detachment

Note: The cgroup must be created under /sys/fs/cgroup/ rather than
/tmp/, because cgroup_get_from_id() uses kernfs_find_and_get_node_by_id()
in the cgroupfs kernfs root. A tmpfs inode is not a valid cgroup ID.

Signed-off-by: zhidao su <suzhidao@xiaomi.com>
---
 tools/testing/selftests/sched_ext/Makefile    |   1 +
 tools/testing/selftests/sched_ext/sub_sched.c | 200 ++++++++++++++++++
 .../selftests/sched_ext/sub_sched_child.bpf.c |  31 +++
 .../sched_ext/sub_sched_parent.bpf.c          |  35 +++
 4 files changed, 267 insertions(+)
 create mode 100644 tools/testing/selftests/sched_ext/sub_sched.c
 create mode 100644 tools/testing/selftests/sched_ext/sub_sched_child.bpf.c
 create mode 100644 tools/testing/selftests/sched_ext/sub_sched_parent.bpf.c

diff --git a/tools/testing/selftests/sched_ext/Makefile b/tools/testing/selftests/sched_ext/Makefile
index 84e4f69b8833..211eef9443a9 100644
--- a/tools/testing/selftests/sched_ext/Makefile
+++ b/tools/testing/selftests/sched_ext/Makefile
@@ -190,6 +190,7 @@ auto-test-targets :=			\
 	select_cpu_dispatch_dbl_dsp	\
 	select_cpu_vtime		\
 	rt_stall			\
+	sub_sched			\
 	test_example			\
 	total_bw			\
 
diff --git a/tools/testing/selftests/sched_ext/sub_sched.c b/tools/testing/selftests/sched_ext/sub_sched.c
new file mode 100644
index 000000000000..4fbd4a60ab03
--- /dev/null
+++ b/tools/testing/selftests/sched_ext/sub_sched.c
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Basic test for sub-sched functionality.
+ *
+ * Tests the ability to attach a child scheduler to a specific cgroup
+ * via the sub_cgroup_id mechanism.
+ *
+ * Copyright (c) 2026 Xiaomi Corporation.
+ */
+
+#include <bpf/bpf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <scx/common.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "sub_sched_parent.bpf.skel.h"
+#include "sub_sched_child.bpf.skel.h"
+#include "scx_test.h"
+
+#define TEST_CGROUP_PATH "/sys/fs/cgroup/test_sub_sched"
+
+struct test_context {
+	struct sub_sched_parent *parent_skel;
+	struct sub_sched_child *child_skel;
+	char cgroup_path[256];
+};
+
+/**
+ * Create a cgroup v2 for testing.
+ * Returns the inode number (which serves as cgroup ID) on success, -1 on error.
+ */
+static u64 create_test_cgroup(const char *path)
+{
+	struct stat st;
+
+	/* Create the test cgroup directory */
+	if (mkdir(path, 0755) < 0) {
+		if (errno != EEXIST) {
+			SCX_ERR("Failed to create cgroup: %s", strerror(errno));
+			return -1;
+		}
+	}
+
+	/* Get the inode number (cgroup ID) */
+	if (stat(path, &st) < 0) {
+		SCX_ERR("Failed to stat cgroup: %s", strerror(errno));
+		return -1;
+	}
+
+	return st.st_ino;
+}
+
+static void cleanup_cgroup(const char *path)
+{
+	if (rmdir(path) < 0 && errno != ENOENT)
+		SCX_ERR("Warning: Failed to cleanup cgroup: %s", strerror(errno));
+}
+
+/**
+ * Setup: Create cgroup, load both parent and child BPF programs.
+ */
+static enum scx_test_status setup(void **ctx)
+{
+	struct test_context *test_ctx;
+	u64 cgroup_id;
+
+	test_ctx = calloc(1, sizeof(*test_ctx));
+	if (!test_ctx)
+		return SCX_TEST_FAIL;
+
+	/* Create test cgroup */
+	snprintf(test_ctx->cgroup_path, sizeof(test_ctx->cgroup_path),
+		 "%s.%d", TEST_CGROUP_PATH, getpid());
+
+	cgroup_id = create_test_cgroup(test_ctx->cgroup_path);
+	if (cgroup_id == (u64)-1) {
+		SCX_ERR("Failed to create test cgroup");
+		free(test_ctx);
+		return SCX_TEST_FAIL;
+	}
+
+	/* Load parent scheduler */
+	test_ctx->parent_skel = sub_sched_parent__open();
+	if (!test_ctx->parent_skel) {
+		SCX_ERR("Failed to open parent BPF skeleton");
+		cleanup_cgroup(test_ctx->cgroup_path);
+		free(test_ctx);
+		return SCX_TEST_FAIL;
+	}
+
+	SCX_ENUM_INIT(test_ctx->parent_skel);
+	if (sub_sched_parent__load(test_ctx->parent_skel)) {
+		SCX_ERR("Failed to load parent BPF program");
+		sub_sched_parent__destroy(test_ctx->parent_skel);
+		cleanup_cgroup(test_ctx->cgroup_path);
+		free(test_ctx);
+		return SCX_TEST_FAIL;
+	}
+
+	/* Load child scheduler */
+	test_ctx->child_skel = sub_sched_child__open();
+	if (!test_ctx->child_skel) {
+		SCX_ERR("Failed to open child BPF skeleton");
+		sub_sched_parent__destroy(test_ctx->parent_skel);
+		cleanup_cgroup(test_ctx->cgroup_path);
+		free(test_ctx);
+		return SCX_TEST_FAIL;
+	}
+
+	/* Set sub_cgroup_id to the test cgroup's inode */
+	test_ctx->child_skel->struct_ops.sub_sched_child_ops->sub_cgroup_id = cgroup_id;
+
+	SCX_ENUM_INIT(test_ctx->child_skel);
+	if (sub_sched_child__load(test_ctx->child_skel)) {
+		SCX_ERR("Failed to load child BPF program");
+		sub_sched_child__destroy(test_ctx->child_skel);
+		sub_sched_parent__destroy(test_ctx->parent_skel);
+		cleanup_cgroup(test_ctx->cgroup_path);
+		free(test_ctx);
+		return SCX_TEST_FAIL;
+	}
+
+	*ctx = test_ctx;
+	return SCX_TEST_PASS;
+}
+
+/**
+ * Run: Test loading parent, then loading child scheduler.
+ *
+ * This tests:
+ * 1. Parent scheduler loads successfully
+ * 2. Child scheduler attaches to the specified cgroup
+ * 3. No crashes or resource leaks
+ */
+static enum scx_test_status run(void *ctx)
+{
+	struct test_context *test_ctx = ctx;
+	struct bpf_link *parent_link;
+	struct bpf_link *child_link;
+
+	/* Attach parent scheduler */
+	parent_link = bpf_map__attach_struct_ops(test_ctx->parent_skel->maps.sub_sched_parent_ops);
+	if (!parent_link) {
+		SCX_ERR("Failed to attach parent scheduler");
+		return SCX_TEST_FAIL;
+	}
+
+	/* Attach child scheduler to the cgroup */
+	child_link = bpf_map__attach_struct_ops(test_ctx->child_skel->maps.sub_sched_child_ops);
+	if (!child_link) {
+		SCX_ERR("Failed to attach child scheduler");
+		bpf_link__destroy(parent_link);
+		return SCX_TEST_FAIL;
+	}
+
+	/* Let both schedulers run briefly to ensure they don't crash */
+	sleep(1);
+
+	/* Detach child first (sub-sched must be detached before parent) */
+	bpf_link__destroy(child_link);
+
+	/* Then detach parent */
+	bpf_link__destroy(parent_link);
+
+	return SCX_TEST_PASS;
+}
+
+static void cleanup(void *ctx)
+{
+	struct test_context *test_ctx = ctx;
+
+	if (!test_ctx)
+		return;
+
+	if (test_ctx->child_skel)
+		sub_sched_child__destroy(test_ctx->child_skel);
+
+	if (test_ctx->parent_skel)
+		sub_sched_parent__destroy(test_ctx->parent_skel);
+
+	cleanup_cgroup(test_ctx->cgroup_path);
+
+	free(test_ctx);
+}
+
+struct scx_test sub_sched_basic = {
+	.name = "sub_sched_basic",
+	.description = "Test basic sub-sched attachment and detachment",
+	.setup = setup,
+	.run = run,
+	.cleanup = cleanup,
+};
+
+REGISTER_SCX_TEST(&sub_sched_basic)
diff --git a/tools/testing/selftests/sched_ext/sub_sched_child.bpf.c b/tools/testing/selftests/sched_ext/sub_sched_child.bpf.c
new file mode 100644
index 000000000000..4df9f049def1
--- /dev/null
+++ b/tools/testing/selftests/sched_ext/sub_sched_child.bpf.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Child scheduler for sub-sched testing.
+ *
+ * This is a minimal scheduler that attaches to a specific cgroup via
+ * sub_cgroup_id. The BPF loader will populate sub_cgroup_id at load time
+ * before attaching.
+ *
+ * Copyright (c) 2026 Xiaomi Corporation.
+ */
+
+#include <scx/common.bpf.h>
+
+char _license[] SEC("license") = "GPL";
+
+__u64 dispatch_count;
+
+void BPF_STRUCT_OPS(sub_sched_child_dispatch, s32 cpu, struct task_struct *prev)
+{
+	/* Minimal dispatch: just return without dispatching anything.
+	 * The kernel will use the default scheduling paths.
+	 */
+	__sync_fetch_and_add(&dispatch_count, 1);
+}
+
+SEC(".struct_ops.link")
+struct sched_ext_ops sub_sched_child_ops = {
+	.name			= "sub_sched_child",
+	.sub_cgroup_id		= 0,  /* Will be set by user space */
+	.dispatch		= (void *)sub_sched_child_dispatch,
+};
diff --git a/tools/testing/selftests/sched_ext/sub_sched_parent.bpf.c b/tools/testing/selftests/sched_ext/sub_sched_parent.bpf.c
new file mode 100644
index 000000000000..8b632b31c682
--- /dev/null
+++ b/tools/testing/selftests/sched_ext/sub_sched_parent.bpf.c
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Parent scheduler for sub-sched testing.
+ *
+ * This scheduler demonstrates the sub_attach/sub_detach ops that allow
+ * a parent scheduler to manage sub-schedulers attached to specific cgroups.
+ *
+ * Copyright (c) 2026 Xiaomi Corporation.
+ */
+
+#include <scx/common.bpf.h>
+
+char _license[] SEC("license") = "GPL";
+
+/* Simple counter for diagnostics */
+__u64 attach_count;
+__u64 detach_count;
+
+s32 BPF_STRUCT_OPS(parent_sub_attach, struct scx_sub_attach_args *args)
+{
+	__sync_fetch_and_add(&attach_count, 1);
+	return 0;
+}
+
+void BPF_STRUCT_OPS(parent_sub_detach, struct scx_sub_detach_args *args)
+{
+	__sync_fetch_and_add(&detach_count, 1);
+}
+
+SEC(".struct_ops.link")
+struct sched_ext_ops sub_sched_parent_ops = {
+	.name			= "sub_sched_parent",
+	.sub_attach		= (void *)parent_sub_attach,
+	.sub_detach		= (void *)parent_sub_detach,
+};
-- 
2.43.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-03-23  7:23 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-03-23  6:18 [PATCH 1/2] selftests/sched_ext: Add basic sub-sched functionality test zhidao su
2026-03-23  6:18 ` [PATCH 2/2] selftests/sched_ext: Add failure-path tests for sub-sched zhidao su
2026-03-23  7:23   ` Tejun Heo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®