mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] sched/deadline: Fix spurious -EPERM for migration-disabled tasks
@ 2026-11-02 13:54 Andrea Parri
  2026-11-02 14:45 ` Aaron Tomlin
  0 siblings, 1 reply; 4+ messages in thread
From: Andrea Parri @ 2026-11-02 13:54 UTC (permalink / raw)
  To: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot
  Cc: Andrea Parri, Dietmar Eggemann, Steven Rostedt, Ben Segall,
	Mel Gorman, Valentin Schneider, K Prateek Nayak,
	Sebastian Andrzej Siewior, Clark Williams, Aaron Tomlin,
	linux-rt-devel, linux-kernel

sched_setattr() can refuse to make a task SCHED_DEADLINE with -EPERM,
even though the task's affinity covers the whole root domain. A retry
can succeed, but the error blames an affinity the task does not have.

__sched_setscheduler() tests p->cpus_ptr, which is not always the task's
affinity. When a task is scheduled out inside a migrate_disable() section,
migrate_disable_switch() points p->cpus_ptr at the current CPU until
migrate_enable(). On PREEMPT_RT every spinlock_t holder is in such a
section, so ordinary kernel work opens the window. Elsewhere, seccomp
filters run in one.

Test p->cpus_mask instead, which keeps the task's affinity and is what
sched_getaffinity() reports. This should be safe because an admitted
task's cpus_mask covers the root domain, so set_cpus_allowed_dl() moves
no bandwidth when migrate_enable() restores p->cpus_ptr.

The following program, run as root, had 553679, 453720 and 424623 of its
1000000 requests refused on three boots of a 4-CPU PREEMPT_RT guest, and
236182, 217174 and 237183 without PREEMPT_RT. With this applied the count
is 0, also with lockdep, and a task pinned to one CPU is still refused:

  #define _GNU_SOURCE
  #include <errno.h>
  #include <linux/filter.h>
  #include <linux/seccomp.h>
  #include <pthread.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <sys/syscall.h>
  #include <unistd.h>

  #define REQUESTS 1000000
  #define INSNS 300

  struct attr {
      uint32_t size, policy;
      uint64_t flags;
      int32_t nice;
      uint32_t prio;
      uint64_t runtime, deadline, period;
  };

  static volatile int stop;
  static volatile pid_t target;

  static long set(struct attr a)
  {
      a.size = sizeof(a);
      return syscall(SYS_sched_setattr, target, &a, 0);
  }

  static void *worker(void *arg)
  {
      struct sock_filter f[INSNS + 1];
      struct sock_fprog prog = { .len = INSNS + 1, .filter = f };
      int i;

      /* Reading the arguments keeps seccomp from caching the result */
      for (i = 0; i < INSNS; i++)
          f[i] = (struct sock_filter)
              BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
                       offsetof(struct seccomp_data, args));
      f[INSNS] = (struct sock_filter)
          BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
      syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog);

      target = syscall(SYS_gettid);
      while (!stop)
          syscall(SYS_getppid);
      return arg;
  }

  static void *hog(void *arg)
  {
      while (!stop)
          ;
      return arg;
  }

  int main(void)
  {
      pthread_t t[9];
      long refused = 0;
      int i;

      pthread_create(&t[0], NULL, worker, NULL);
      while (!target)
          usleep(100);
      for (i = 1; i < 9; i++)
          pthread_create(&t[i], NULL, hog, NULL);
      for (i = 0; i < REQUESTS; i++) {
          /* SCHED_DEADLINE, 2us every 1s */
          if (set((struct attr){ .policy = 6, .runtime = 2000,
                                 .deadline = 1000000000,
                                 .period = 1000000000 }) && errno == EPERM)
              refused++;
          set((struct attr){ .policy = 0 }); /* SCHED_OTHER */
      }
      stop = 1;
      for (i = 0; i < 9; i++)
          pthread_join(t[i], NULL);
      printf("%ld of %d requests refused\n", refused, REQUESTS);
      return 0;
  }

An LLM helped find this bug and write this patch and its test.

Fixes: af449901b84c ("sched: Add migrate_disable()")
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
Cc: stable@vger.kernel.org
---
This textually conflicts with [1], which rewrites this line and keeps
p->cpus_ptr. Whichever goes second needs to keep &p->cpus_mask.

Also tested on PREEMPT_RT with lockdep by holding a 1ms/10ms reservation
for 3ms at a time, so that tasks admitted while migration-disabled reach
migrate_enable() as SCHED_DEADLINE: no warnings, total_bw unchanged, and
none of 6788 requests refused. Without this patch, 826 of 6645 are refused.

Other !PREEMPT_RT migrate_disable() users were not tested.

[1] https://lore.kernel.org/20261030151307.768523-2-atomlin@atomlin.com/

 kernel/sched/syscalls.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
index 45a127deeadd9..fb8f332074b88 100644
--- a/kernel/sched/syscalls.c
+++ b/kernel/sched/syscalls.c
@@ -628,7 +628,7 @@ int __sched_setscheduler(struct task_struct *p,
 			 * the entire root_domain to become SCHED_DEADLINE. We
 			 * will also fail if there's no bandwidth available.
 			 */
-			if (!cpumask_subset(span, p->cpus_ptr) ||
+			if (!cpumask_subset(span, &p->cpus_mask) ||
 			    rq->rd->dl_bw.bw == 0) {
 				retval = -EPERM;
 				goto unlock;
-- 
2.53.0


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

* Re: [PATCH] sched/deadline: Fix spurious -EPERM for migration-disabled tasks
  2026-11-02 13:54 [PATCH] sched/deadline: Fix spurious -EPERM for migration-disabled tasks Andrea Parri
@ 2026-11-02 14:45 ` Aaron Tomlin
  2026-11-02 15:14   ` Andrea Parri
  0 siblings, 1 reply; 4+ messages in thread
From: Aaron Tomlin @ 2026-11-02 14:45 UTC (permalink / raw)
  To: Andrea Parri
  Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, K Prateek Nayak, Sebastian Andrzej Siewior,
	Clark Williams, linux-rt-devel, linux-kernel

On Mon, Nov 02, 2026 at 02:54:19PM +0100, Andrea Parri wrote:
> sched_setattr() can refuse to make a task SCHED_DEADLINE with -EPERM,
> even though the task's affinity covers the whole root domain. A retry
> can succeed, but the error blames an affinity the task does not have.
> 
> __sched_setscheduler() tests p->cpus_ptr, which is not always the task's
> affinity. When a task is scheduled out inside a migrate_disable() section,
> migrate_disable_switch() points p->cpus_ptr at the current CPU until
> migrate_enable(). On PREEMPT_RT every spinlock_t holder is in such a
> section, so ordinary kernel work opens the window. Elsewhere, seccomp
> filters run in one.
> 
> Test p->cpus_mask instead, which keeps the task's affinity and is what
> sched_getaffinity() reports. This should be safe because an admitted
> task's cpus_mask covers the root domain, so set_cpus_allowed_dl() moves
> no bandwidth when migrate_enable() restores p->cpus_ptr.
> 
> The following program, run as root, had 553679, 453720 and 424623 of its
> 1000000 requests refused on three boots of a 4-CPU PREEMPT_RT guest, and
> 236182, 217174 and 237183 without PREEMPT_RT. With this applied the count
> is 0, also with lockdep, and a task pinned to one CPU is still refused:
> 
>   #define _GNU_SOURCE
>   #include <errno.h>
>   #include <linux/filter.h>
>   #include <linux/seccomp.h>
>   #include <pthread.h>
>   #include <stddef.h>
>   #include <stdint.h>
>   #include <stdio.h>
>   #include <sys/syscall.h>
>   #include <unistd.h>
> 
>   #define REQUESTS 1000000
>   #define INSNS 300
> 
>   struct attr {
>       uint32_t size, policy;
>       uint64_t flags;
>       int32_t nice;
>       uint32_t prio;
>       uint64_t runtime, deadline, period;
>   };
> 
>   static volatile int stop;
>   static volatile pid_t target;
> 
>   static long set(struct attr a)
>   {
>       a.size = sizeof(a);
>       return syscall(SYS_sched_setattr, target, &a, 0);
>   }
> 
>   static void *worker(void *arg)
>   {
>       struct sock_filter f[INSNS + 1];
>       struct sock_fprog prog = { .len = INSNS + 1, .filter = f };
>       int i;
> 
>       /* Reading the arguments keeps seccomp from caching the result */
>       for (i = 0; i < INSNS; i++)
>           f[i] = (struct sock_filter)
>               BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
>                        offsetof(struct seccomp_data, args));
>       f[INSNS] = (struct sock_filter)
>           BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW);
>       syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog);
> 
>       target = syscall(SYS_gettid);
>       while (!stop)
>           syscall(SYS_getppid);
>       return arg;
>   }
> 
>   static void *hog(void *arg)
>   {
>       while (!stop)
>           ;
>       return arg;
>   }
> 
>   int main(void)
>   {
>       pthread_t t[9];
>       long refused = 0;
>       int i;
> 
>       pthread_create(&t[0], NULL, worker, NULL);
>       while (!target)
>           usleep(100);
>       for (i = 1; i < 9; i++)
>           pthread_create(&t[i], NULL, hog, NULL);
>       for (i = 0; i < REQUESTS; i++) {
>           /* SCHED_DEADLINE, 2us every 1s */
>           if (set((struct attr){ .policy = 6, .runtime = 2000,
>                                  .deadline = 1000000000,
>                                  .period = 1000000000 }) && errno == EPERM)
>               refused++;
>           set((struct attr){ .policy = 0 }); /* SCHED_OTHER */
>       }
>       stop = 1;
>       for (i = 0; i < 9; i++)
>           pthread_join(t[i], NULL);
>       printf("%ld of %d requests refused\n", refused, REQUESTS);
>       return 0;
>   }
> 
> An LLM helped find this bug and write this patch and its test.
> 
> Fixes: af449901b84c ("sched: Add migrate_disable()")
> Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
> Cc: stable@vger.kernel.org
> ---
> This textually conflicts with [1], which rewrites this line and keeps
> p->cpus_ptr. Whichever goes second needs to keep &p->cpus_mask.
> 
> Also tested on PREEMPT_RT with lockdep by holding a 1ms/10ms reservation
> for 3ms at a time, so that tasks admitted while migration-disabled reach
> migrate_enable() as SCHED_DEADLINE: no warnings, total_bw unchanged, and
> none of 6788 requests refused. Without this patch, 826 of 6645 are refused.
> 
> Other !PREEMPT_RT migrate_disable() users were not tested.
> 
> [1] https://lore.kernel.org/20261030151307.768523-2-atomlin@atomlin.com/
> 
>  kernel/sched/syscalls.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
> index 45a127deeadd9..fb8f332074b88 100644
> --- a/kernel/sched/syscalls.c
> +++ b/kernel/sched/syscalls.c
> @@ -628,7 +628,7 @@ int __sched_setscheduler(struct task_struct *p,
>  			 * the entire root_domain to become SCHED_DEADLINE. We
>  			 * will also fail if there's no bandwidth available.
>  			 */
> -			if (!cpumask_subset(span, p->cpus_ptr) ||
> +			if (!cpumask_subset(span, &p->cpus_mask) ||
>  			    rq->rd->dl_bw.bw == 0) {
>  				retval = -EPERM;
>  				goto unlock;
> -- 
> 2.53.0
> 

Hi Andrea,

Do you mind if I include your patch in my series [1]?

Also, please detail the LLM used as per Documentation/process/coding-assistants.rst:

    Assisted-by: LLM [TOOL1] [TOOL2]


[1]: https://lore.kernel.org/all/20261030151307.768523-1-atomlin@atomlin.com/

Kind regards,
-- 
Aaron Tomlin

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

* Re: [PATCH] sched/deadline: Fix spurious -EPERM for migration-disabled tasks
  2026-11-02 14:45 ` Aaron Tomlin
@ 2026-11-02 15:14   ` Andrea Parri
  2026-11-02 17:00     ` Aaron Tomlin
  0 siblings, 1 reply; 4+ messages in thread
From: Andrea Parri @ 2026-11-02 15:14 UTC (permalink / raw)
  To: Aaron Tomlin
  Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, K Prateek Nayak, Sebastian Andrzej Siewior,
	Clark Williams, linux-rt-devel, linux-kernel

On Mon, Nov 02, 2026 at 09:45:30AM -0500, Aaron Tomlin wrote:

[...]

> Do you mind if I include your patch in my series [1]?

Thanks for the offer, but I'd rather keep this one standalone: it's a
stable fix, and it shouldn't have to wait for the debugfs series.  If it
goes in first, your 1/6 just needs to keep &p->cpus_mask on that line:

	if (!cpumask_subset(rd->span, &p->cpus_mask) ||
	    rd->dl_bw.bw == 0) {

If the maintainers would rather have it in your series, I'm fine with
that, as the first patch and with its authorship and its Fixes and
stable tags unchanged.

> Also, please detail the LLM used as per Documentation/process/coding-assistants.rst:
>
>     Assisted-by: LLM [TOOL1] [TOOL2]

That tag is gone as of v7.4-rc1, which this patch is based on: commit
1e035297ffda ("docs: remove Assisted-by references") dropped it from
coding-assistants.rst and checkpatch.  tip/sched/core is still on an
older base, where the docs have it.

Per Documentation/process/generated-content.rst, the tool was Claude
Code with Claude Opus 5.5.  It helped find the bug and write the patch,
its changelog and its test, and I reviewed the result.

Thanks,
  Andrea

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

* Re: [PATCH] sched/deadline: Fix spurious -EPERM for migration-disabled tasks
  2026-11-02 15:14   ` Andrea Parri
@ 2026-11-02 17:00     ` Aaron Tomlin
  0 siblings, 0 replies; 4+ messages in thread
From: Aaron Tomlin @ 2026-11-02 17:00 UTC (permalink / raw)
  To: Andrea Parri
  Cc: Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot,
	Dietmar Eggemann, Steven Rostedt, Ben Segall, Mel Gorman,
	Valentin Schneider, K Prateek Nayak, Sebastian Andrzej Siewior,
	Clark Williams, linux-rt-devel, linux-kernel

On Mon, Nov 02, 2026 at 04:14:56PM +0100, Andrea Parri wrote:
> On Mon, Nov 02, 2026 at 09:45:30AM -0500, Aaron Tomlin wrote:
> 
> [...]
> 
> > Do you mind if I include your patch in my series [1]?
> 
> Thanks for the offer, but I'd rather keep this one standalone: it's a
> stable fix, and it shouldn't have to wait for the debugfs series.  If it
> goes in first, your 1/6 just needs to keep &p->cpus_mask on that line:
> 
> 	if (!cpumask_subset(rd->span, &p->cpus_mask) ||
> 	    rd->dl_bw.bw == 0) {
> 
> If the maintainers would rather have it in your series, I'm fine with
> that, as the first patch and with its authorship and its Fixes and
> stable tags unchanged.

Acknowledged.

Kind regards,
-- 
Aaron Tomlin

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

end of thread, other threads:[~2026-11-02 17:00 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-02 13:54 [PATCH] sched/deadline: Fix spurious -EPERM for migration-disabled tasks Andrea Parri
2026-11-02 14:45 ` Aaron Tomlin
2026-11-02 15:14   ` Andrea Parri
2026-11-02 17:00     ` Aaron Tomlin

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®