mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: rahadbhuiya <rahadbhuiya2021@gmail.com>
To: Tejun Heo <tj@kernel.org>, Andrea Righi <arighi@nvidia.com>
Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org,
	rahadbhuiya <rahadbhuiya2021@gmail.com>
Subject: [PATCH] tools/sched_ext: Add scx_priority dual-queue priority CPU scheduler
Date: Tue, 22 Sep 2026 14:45:57 +0600	[thread overview]
Message-ID: <20260922084557.532-1-rahadbhuiya2021@gmail.com> (raw)

Add scx_priority, a dual-queue priority CPU scheduler using sched_ext.
It differentiates between latency-sensitive/interactive tasks (nice < 0)
and normal/batch tasks, dispatching high-priority tasks with boosted
slices and draining the high-priority queue first upon core availability.

- Add scx_priority.bpf.c with two DSQs (high priority and normal).
- Add scx_priority.c userspace monitor tracking live throughput.
- Update Makefile and README.md.

Signed-off-by: rahadbhuiya <rahadbhuiya2021@gmail.com>
---
 tools/sched_ext/Makefile           |   2 +-
 tools/sched_ext/README.md          |   7 ++
 tools/sched_ext/scx_priority.bpf.c | 117 ++++++++++++++++++++++++++
 tools/sched_ext/scx_priority.c     | 128 +++++++++++++++++++++++++++++
 4 files changed, 253 insertions(+), 1 deletion(-)
 create mode 100644 tools/sched_ext/scx_priority.bpf.c
 create mode 100644 tools/sched_ext/scx_priority.c

diff --git a/tools/sched_ext/Makefile b/tools/sched_ext/Makefile
index 21554f089692..41b80e164cda 100644
--- a/tools/sched_ext/Makefile
+++ b/tools/sched_ext/Makefile
@@ -191,7 +191,7 @@ $(INCLUDE_DIR)/%.bpf.skel.h: $(SCXOBJ_DIR)/%.bpf.o $(INCLUDE_DIR)/vmlinux.h $(BP
 
 SCX_COMMON_DEPS := include/scx/common.h include/scx/user_exit_info.h | $(BINDIR)
 
-c-sched-targets = scx_simple scx_cpu0 scx_qmap scx_central scx_flatcg scx_userland scx_pair scx_sdt
+c-sched-targets = scx_simple scx_cpu0 scx_qmap scx_central scx_flatcg scx_userland scx_pair scx_sdt scx_priority
 
 $(addprefix $(BINDIR)/,$(c-sched-targets)): \
 	$(BINDIR)/%: \
diff --git a/tools/sched_ext/README.md b/tools/sched_ext/README.md
index 0ee5a3d997e5..18b80eacc28a 100644
--- a/tools/sched_ext/README.md
+++ b/tools/sched_ext/README.md
@@ -164,6 +164,13 @@ scx_simple can be run in either global weighted vtime mode, or FIFO mode.
 Though very simple, in limited scenarios, this scheduler can perform reasonably
 well on single-socket systems with a unified L3 cache.
 
+## scx_priority
+
+A dual-queue priority scheduler that separates latency-sensitive and interactive
+tasks from normal/batch tasks. Tasks with higher priority (nice < 0) are queued
+to a dedicated high-priority DSQ with boosted time slices and drained first upon
+dispatch.
+
 ## scx_qmap
 
 Another simple, yet slightly more complex scheduler that provides an example of
diff --git a/tools/sched_ext/scx_priority.bpf.c b/tools/sched_ext/scx_priority.bpf.c
new file mode 100644
index 000000000000..816b197c4019
--- /dev/null
+++ b/tools/sched_ext/scx_priority.bpf.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A dual-queue priority scheduler based on sched_ext.
+ *
+ * Dispatches latency-sensitive / interactive tasks (nice < 0) to a high-priority
+ * DSQ, and batch / normal tasks to a standard DSQ. When a CPU core becomes
+ * available, the high-priority queue is drained first before serving normal tasks.
+ *
+ * Copyright (c) 2026 Rahad Bhuiya <rahadbhuiya2021@gmail.com>
+ */
+#include <scx/common.bpf.h>
+
+char _license[] SEC("license") = "GPL";
+
+#define PRIO_DSQ_HIGH	0
+#define PRIO_DSQ_LOW	1
+
+/*
+ * Stats tracking:
+ * [0] - High priority / interactive tasks queued
+ * [1] - Standard / batch tasks queued
+ */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(u64));
+	__uint(max_entries, 2);
+} stats SEC(".maps");
+
+static void stat_inc(u32 idx)
+{
+	u64 *cnt_p = bpf_map_lookup_elem(&stats, &idx);
+
+	if (cnt_p)
+		(*cnt_p)++;
+}
+
+static bool is_high_prio(const struct task_struct *p)
+{
+	/*
+	 * In the Linux kernel, static_prio maps nice -20..19 to 100..139.
+	 * Default nice 0 corresponds to static_prio 120. Tasks with nice < 0
+	 * (static_prio < 120) or real-time policies are prioritized.
+	 */
+	return p->static_prio < 120;
+}
+
+s32 BPF_STRUCT_OPS(prio_select_cpu, struct task_struct *p, s32 prev_cpu, u64 wake_flags)
+{
+	bool is_idle = false;
+	s32 cpu;
+
+	cpu = scx_bpf_select_cpu_dfl(p, prev_cpu, wake_flags, &is_idle);
+	if (is_idle) {
+		u64 slice = is_high_prio(p) ? (2 * SCX_SLICE_DFL) : SCX_SLICE_DFL;
+
+		stat_inc(is_high_prio(p) ? 0 : 1);
+		scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, slice, 0);
+	}
+
+	return cpu;
+}
+
+void BPF_STRUCT_OPS(prio_enqueue, struct task_struct *p, u64 enq_flags)
+{
+	if (is_high_prio(p)) {
+		stat_inc(0);
+		scx_bpf_dsq_insert(p, PRIO_DSQ_HIGH, 2 * SCX_SLICE_DFL, enq_flags);
+	} else {
+		stat_inc(1);
+		scx_bpf_dsq_insert(p, PRIO_DSQ_LOW, SCX_SLICE_DFL, enq_flags);
+	}
+}
+
+void BPF_STRUCT_OPS(prio_dispatch, s32 cpu, struct task_struct *prev)
+{
+	/* First drain high-priority tasks if any are waiting */
+	if (scx_bpf_dsq_move_to_local(PRIO_DSQ_HIGH, 0))
+		return;
+
+	/* Otherwise drain standard priority tasks */
+	scx_bpf_dsq_move_to_local(PRIO_DSQ_LOW, 0);
+}
+
+s32 BPF_STRUCT_OPS_SLEEPABLE(prio_init)
+{
+	int ret;
+
+	ret = scx_bpf_create_dsq(PRIO_DSQ_HIGH, -1);
+	if (ret) {
+		scx_bpf_error("failed to create high priority DSQ (%d)", ret);
+		return ret;
+	}
+
+	ret = scx_bpf_create_dsq(PRIO_DSQ_LOW, -1);
+	if (ret) {
+		scx_bpf_error("failed to create low priority DSQ (%d)", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+UEI_DEFINE(uei);
+
+void BPF_STRUCT_OPS(prio_exit, struct scx_exit_info *ei)
+{
+	UEI_RECORD(uei, ei);
+}
+
+SCX_OPS_DEFINE(priority_ops,
+	       .select_cpu	= (void *)prio_select_cpu,
+	       .enqueue		= (void *)prio_enqueue,
+	       .dispatch	= (void *)prio_dispatch,
+	       .init		= (void *)prio_init,
+	       .exit		= (void *)prio_exit,
+	       .name		= "priority");
diff --git a/tools/sched_ext/scx_priority.c b/tools/sched_ext/scx_priority.c
new file mode 100644
index 000000000000..3cf975be3a1e
--- /dev/null
+++ b/tools/sched_ext/scx_priority.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Userspace controller and monitor for scx_priority scheduler.
+ *
+ * Copyright (c) 2026 Rahad Bhuiya <rahadbhuiya2021@gmail.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <assert.h>
+#include <libgen.h>
+#include <bpf/bpf.h>
+#include <scx/common.h>
+#include "scx_priority.bpf.skel.h"
+
+const char help_fmt[] =
+"A dual-queue priority sched_ext scheduler.\n"
+"\n"
+"Usage: %s [-i INTERVAL] [-v] [-h]\n"
+"\n"
+"  -i INTERVAL   Stats monitoring interval in seconds (default: 1)\n"
+"  -v            Print libbpf debug messages\n"
+"  -h            Display this help and exit\n";
+
+static bool verbose;
+static sig_atomic_t exit_req;
+
+static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
+{
+	if (level == LIBBPF_DEBUG && !verbose)
+		return 0;
+	return vfprintf(stderr, format, args);
+}
+
+static void sigint_handler(int sig)
+{
+	exit_req = 1;
+}
+
+static void read_stats(struct scx_priority *skel, __u64 *stats)
+{
+	int nr_cpus = libbpf_num_possible_cpus();
+	__u64 *cnts[2];
+	__u32 idx;
+
+	assert(nr_cpus > 0);
+	cnts[0] = calloc(nr_cpus, sizeof(__u64));
+	cnts[1] = calloc(nr_cpus, sizeof(__u64));
+	if (!cnts[0] || !cnts[1]) {
+		free(cnts[0]);
+		free(cnts[1]);
+		return;
+	}
+
+	memset(stats, 0, sizeof(stats[0]) * 2);
+
+	for (idx = 0; idx < 2; idx++) {
+		int ret, cpu;
+
+		ret = bpf_map_lookup_elem(bpf_map__fd(skel->maps.stats),
+					  &idx, cnts[idx]);
+		if (ret < 0)
+			continue;
+		for (cpu = 0; cpu < nr_cpus; cpu++)
+			stats[idx] += cnts[idx][cpu];
+	}
+
+	free(cnts[0]);
+	free(cnts[1]);
+}
+
+int main(int argc, char **argv)
+{
+	struct scx_priority *skel;
+	struct bpf_link *link;
+	__s32 opt;
+	__u64 ecode;
+	int interval = 1;
+
+	libbpf_set_print(libbpf_print_fn);
+	signal(SIGINT, sigint_handler);
+	signal(SIGTERM, sigint_handler);
+
+restart:
+	optind = 1;
+	skel = SCX_OPS_OPEN(priority_ops, scx_priority);
+
+	while ((opt = getopt(argc, argv, "i:vh")) != -1) {
+		switch (opt) {
+		case 'i':
+			interval = atoi(optarg);
+			if (interval <= 0)
+				interval = 1;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			fprintf(stderr, help_fmt, basename(argv[0]));
+			return opt != 'h';
+		}
+	}
+
+	SCX_OPS_LOAD(skel, priority_ops, scx_priority, uei);
+	link = SCX_OPS_ATTACH(skel, priority_ops, scx_priority);
+
+	printf("scx_priority started (interval: %ds). Press Ctrl-C to stop.\n", interval);
+	printf("%-15s %-15s %-15s\n", "HIGH_PRIO(UI)", "LOW_PRIO(BATCH)", "TOTAL_DISPATCH");
+
+	while (!exit_req && !UEI_EXITED(skel, uei)) {
+		__u64 stats[2];
+
+		read_stats(skel, stats);
+		printf("%-15llu %-15llu %-15llu\n",
+		       stats[0], stats[1], stats[0] + stats[1]);
+		fflush(stdout);
+		sleep(interval);
+	}
+
+	bpf_link__destroy(link);
+	ecode = UEI_REPORT(skel, uei);
+	scx_priority__destroy(skel);
+
+	if (!exit_req && UEI_ECODE_RESTART(ecode))
+		goto restart;
+	return 0;
+}
-- 
2.54.0.windows.1


             reply	other threads:[~2026-09-22  8:46 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-22  8:45 rahadbhuiya [this message]
2026-09-22 13:48 ` Andrea Righi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260922084557.532-1-rahadbhuiya2021@gmail.com \
    --to=rahadbhuiya2021@gmail.com \
    --cc=arighi@nvidia.com \
    --cc=bpf@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=tj@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®