[PATCH] sched/fair: Add per-cgroup CPU bandwidth slice control via cpuctl.slice_ns

From: Fang Xiang

Date: Sun Apr 12 2026 - 22:54:07 EST


Introduce a new cgroup interface "cpu.slice_ns" that allows setting a
per-task-group CPU scheduling slice in nanoseconds. This enables
fine-grained control over CFS scheduler time slices on a per-cgroup
basis, complementing the existing cpu.weight and cpu.max controls.

The slice value is enforced within 0.1ms to 100ms. When set, all tasks
within the cgroup inherit the custom slice, and newly added tasks are
assigned the group's slice during sched_change_group().

Usage:

# Set 10ms slice for a cgroup
echo 10000000 > cpu.slice_ns

# Read current slice (0 means default)
cat cpu.slice_ns

Signed-off-by: Fang Xiang <fangxiang3@xxxxxxxxxx>
---
kernel/sched/core.c | 24 ++++++++++++++++++++++++
kernel/sched/fair.c | 34 ++++++++++++++++++++++++++++++++++
kernel/sched/sched.h | 4 ++++
3 files changed, 62 insertions(+)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 582c3847f483..14986786f98e 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -9197,6 +9197,13 @@ static void sched_change_group(struct task_struct *tsk)
tg = autogroup_task_group(tsk, tg);
tsk->sched_task_group = tg;

+ if (tg->slice_ns) {
+ tsk->se.custom_slice = 1;
+ tsk->se.slice = tg->slice_ns;
+ } else {
+ tsk->se.custom_slice = 0;
+ }
+
#ifdef CONFIG_FAIR_GROUP_SCHED
if (tsk->sched_class->task_change_group)
tsk->sched_class->task_change_group(tsk);
@@ -9974,6 +9981,18 @@ static int cpu_idle_write_s64(struct cgroup_subsys_state *css,
}
#endif /* CONFIG_GROUP_SCHED_WEIGHT */

+static u64 slice_ns_read_u64(struct cgroup_subsys_state *css,
+ struct cftype *cft)
+{
+ return css_tg(css)->slice_ns;
+}
+
+static int slice_ns_write_u64(struct cgroup_subsys_state *css,
+ struct cftype *cft, u64 slice)
+{
+ return sched_group_set_slice(css, slice);
+}
+
static struct cftype cpu_legacy_files[] = {
#ifdef CONFIG_GROUP_SCHED_WEIGHT
{
@@ -10028,6 +10047,11 @@ static struct cftype cpu_legacy_files[] = {
.write = cpu_uclamp_max_write,
},
#endif
+ {
+ .name = "slice_ns",
+ .read_u64 = slice_ns_read_u64,
+ .write_u64 = slice_ns_write_u64,
+ },
{ } /* Terminate */
};

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 292141f4aaa5..081ba87ca435 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -13798,6 +13798,40 @@ int sched_group_set_shares(struct task_group *tg, unsigned long shares)
return ret;
}

+int sched_group_set_slice(struct cgroup_subsys_state *css, u64 slice)
+{
+ struct css_task_iter it;
+ struct task_struct *task;
+ struct task_group *tg = css_tg(css);
+
+ if (tg == &root_task_group)
+ return -EINVAL;
+
+ if (slice < NSEC_PER_MSEC / 10 ||
+ slice > NSEC_PER_MSEC * 100)
+ return -EINVAL;
+
+ mutex_lock(&shares_mutex);
+
+ if (tg->slice_ns == slice) {
+ mutex_unlock(&shares_mutex);
+ return 0;
+ }
+
+ tg->slice_ns = slice;
+
+ css_task_iter_start(css, 0, &it);
+ while ((task = css_task_iter_next(&it))) {
+ task->se.custom_slice = 1;
+ task->se.slice = slice;
+ }
+ css_task_iter_end(&it);
+
+ mutex_unlock(&shares_mutex);
+
+ return 0;
+}
+
int sched_group_set_idle(struct task_group *tg, long idle)
{
int i;
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index ed37ab9209e5..0ce806062864 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -512,6 +512,7 @@ struct task_group {
#endif

struct cfs_bandwidth cfs_bandwidth;
+ u64 slice_ns;

#ifdef CONFIG_UCLAMP_TASK_GROUP
/* The two decimal precision [%] value requested from user-space */
@@ -611,9 +612,12 @@ extern int sched_group_set_idle(struct task_group *tg, long idle);

extern void set_task_rq_fair(struct sched_entity *se,
struct cfs_rq *prev, struct cfs_rq *next);
+
+extern int sched_group_set_slice(struct cgroup_subsys_state *css, u64 slice);
#else /* !CONFIG_FAIR_GROUP_SCHED: */
static inline int sched_group_set_shares(struct task_group *tg, unsigned long shares) { return 0; }
static inline int sched_group_set_idle(struct task_group *tg, long idle) { return 0; }
+static inline int sched_group_set_slice(struct cgroup_subsys_state *css, u64 slice) { return 0; }
#endif /* !CONFIG_FAIR_GROUP_SCHED */

#else /* !CONFIG_CGROUP_SCHED: */
--
2.34.1