[PATCH v2] sched: Reject policy changes with SCHED_FLAG_KEEP_PARAMS

From: Andrea Righi

Date: Thu Jul 30 2026 - 10:18:34 EST


SCHED_FLAG_KEEP_PARAMS prevents __sched_setscheduler() from applying the
requested scheduler parameters, policy and class. However, a different
requested policy can still trigger deadline bandwidth accounting and
scheduling class callbacks.

Reject policy changes with SCHED_FLAG_KEEP_PARAMS while holding the
task's rq lock. Also skip deadline bandwidth accounting and class
transition callbacks when the guarded scheduler update cannot apply the
corresponding changes.

This prevents deadline bandwidth from being accounted without changing
the task and avoids class callbacks when p->sched_class remains
unchanged.

Fixes: a509a7cd7974 ("sched/uclamp: Extend sched_setattr() to support utilization clamping")
Fixes: 637b0682821b ("sched: Fold sched_class::switch{ing,ed}_{to,from}() into the change pattern")
Suggested-by: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: K Prateek Nayak <kprateek.nayak@xxxxxxx>
Signed-off-by: Andrea Righi <arighi@xxxxxxxxxx>
---
Changes in v2:
- Reject policy changes with SCHED_FLAG_KEEP_PARAMS (Peter Zijlstra)
- Perform the policy check under the rq lock to avoid racing with a concurrent
policy change
- Link to v1: https://lore.kernel.org/all/20260730055011.2267333-1-arighi@xxxxxxxxxx/

kernel/sched/syscalls.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
index b215b0ead9a60..8fb8474d0a0ec 100644
--- a/kernel/sched/syscalls.c
+++ b/kernel/sched/syscalls.c
@@ -645,12 +645,19 @@ int __sched_setscheduler(struct task_struct *p,
goto recheck;
}

+ /* KEEP_PARAMS only makes sense if the scheduling policy is unchanged */
+ if ((attr->sched_flags & SCHED_FLAG_KEEP_PARAMS) && policy != p->policy) {
+ retval = -EINVAL;
+ goto unlock;
+ }
+
/*
* If setscheduling to SCHED_DEADLINE (or changing the parameters
* of a SCHED_DEADLINE task) we need to check if enough bandwidth
* is available.
*/
- if ((dl_policy(policy) || dl_task(p)) && sched_dl_overflow(p, policy, attr)) {
+ if (!(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS) &&
+ (dl_policy(policy) || dl_task(p)) && sched_dl_overflow(p, policy, attr)) {
retval = -EBUSY;
goto unlock;
}
@@ -675,7 +682,7 @@ int __sched_setscheduler(struct task_struct *p,
prev_class = p->sched_class;
next_class = __setscheduler_class(policy, newprio);

- if (prev_class != next_class)
+ if (!(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS) && prev_class != next_class)
queue_flags |= DEQUEUE_CLASS;

scoped_guard (sched_change, p, queue_flags) {
--
2.55.0