[PATCH 3/4] sched: Reject SCHED_FLAG_KEEP_PARAMS when it would change scheduling class

From: Juri Lelli

Date: Tue Nov 03 2026 - 08:28:33 EST


SCHED_FLAG_KEEP_PARAMS was introduced for uclamp (a509a7cd7974) to let
userspace adjust utilization clamping without disturbing existing
scheduling parameters. The flag skips __setscheduler_params() and the
sched_class assignment entirely.

However, when a sched_setattr() call requests a policy change that
would switch the scheduling class (e.g., SCHED_OTHER on a
SCHED_DEADLINE task) while also setting KEEP_PARAMS, the result is
contradictory: DEQUEUE_CLASS is set (because prev_class != next_class),
causing sched_change_begin() to call switched_from_dl(), but the actual
class change never happens. This leaves the task in the original class
with its bandwidth accounting corrupted.

For SCHED_DEADLINE tasks, each such call spuriously releases running_bw
and rq_bw via switched_from_dl(), leading to accounting underflow.

Reject the contradictory combination early with -EINVAL. This does not
affect the intended uclamp use case, which always pairs KEEP_PARAMS
with KEEP_POLICY (keeping the same class).

Fixes: a509a7cd7974 ("sched/uclamp: Extend sched_setattr() to support utilization clamping")
Link: https://lore.kernel.org/all/2b5fb3f22bca6aa383be8f4781923392@xxxxxxxxxx/
Signed-off-by: Juri Lelli <juri.lelli@xxxxxxxxxx>
---
kernel/sched/syscalls.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
index b215b0ead9a60..014697d454525 100644
--- a/kernel/sched/syscalls.c
+++ b/kernel/sched/syscalls.c
@@ -675,8 +675,19 @@ int __sched_setscheduler(struct task_struct *p,
prev_class = p->sched_class;
next_class = __setscheduler_class(policy, newprio);

- if (prev_class != next_class)
+ if (prev_class != next_class) {
+ /*
+ * SCHED_FLAG_KEEP_PARAMS prevents the class change from
+ * actually happening (it skips __setscheduler_params and
+ * the sched_class assignment). Reject contradictory
+ * requests that ask for a class change with KEEP_PARAMS.
+ */
+ if (attr->sched_flags & SCHED_FLAG_KEEP_PARAMS) {
+ retval = -EINVAL;
+ goto unlock;
+ }
queue_flags |= DEQUEUE_CLASS;
+ }

scoped_guard (sched_change, p, queue_flags) {


--
2.56.0