mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Tejun Heo <tj@kernel.org>
To: Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@redhat.com>,
	Juri Lelli <juri.lelli@redhat.com>,
	Vincent Guittot <vincent.guittot@linaro.org>
Cc: Dietmar Eggemann <dietmar.eggemann@arm.com>,
	Steven Rostedt <rostedt@goodmis.org>,
	Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
	Valentin Schneider <vschneid@redhat.com>,
	K Prateek Nayak <kprateek.nayak@amd.com>,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH sched/urgent] sched/deadline: Fix stale rq->dl_server after a server pick returns RETRY_TASK
Date: Fri, 30 Oct 2026 15:19:17 -1000	[thread overview]
Message-ID: <6db076ee2562b5fb84d70ed59abe670e@kernel.org> (raw)
In-Reply-To: <20261030221842.4071610-1-tj@kernel.org>

Hello,

This is what I should have sent: a bug report with a reproducer, not the
patch.

__pick_task_dl() records the server in rq->dl_server on RETRY_TASK too, and
__pick_next_task() clears it only above its restart label, so the restarted
pick hands the stale server to whatever task it takes. This became reachable
with b3a2dfa8b42e ("sched/fair: Add newidle balance to pick_task_fair()"),
which makes fair_server_pick_task() return RETRY_TASK when a higher-class
task was enqueued while sched_balance_newidle() had the rq lock dropped.

On mainline abf8c33470f7 in an 8-CPU VM, the reproducer below (60s, 500us
delay) hands the fair server to RT tasks:

  stale rq->dl_server handed to stress-ng-switc/1517 (rt_sched_class)

Six runs counted 8, 44, 0, 7, 24 and 22 hand-offs. No runtime effect of the
stale pointer was observed.

The debug patch widens the newidle lock drop inside a dl server's pick by
fair.dbg_newidle_delay_us and reports the hand-offs. The script needs
stress-ng and root.

  --- a/kernel/sched/core.c
  +++ b/kernel/sched/core.c
  @@ -6240,6 +6240,12 @@ restart:
   		if (unlikely(p == RETRY_TASK))
   			goto restart;
   		if (p) {
  +			static DEFINE_RATELIMIT_STATE(dbg_rs, 5 * HZ, 10);
  +
  +			/* REPRO: only a dl server pick in this pass sets rq->dl_server */
  +			if (rq->dl_server && class != &dl_sched_class && __ratelimit(&dbg_rs))
  +				printk_deferred(KERN_WARNING "stale rq->dl_server handed to %s/%d (%ps)\n",
  +						p->comm, p->pid, class);
   			put_prev_set_next_task(rq, rq->donor, p);
   			return p;
   		}
  --- a/kernel/sched/deadline.c
  +++ b/kernel/sched/deadline.c
  @@ -2813,6 +2813,9 @@ static struct sched_dl_entity *pick_next
   	return __node_2_dle(left);
   }
   
  +/* REPRO: lets sched_balance_newidle() widen its lock drop in a server pick */
  +DEFINE_PER_CPU(bool, dbg_in_dl_server_pick);
  +
   /*
    * __pick_next_task_dl - Helper to pick the next -deadline task to run.
    * @rq: The runqueue to pick the next task from.
  @@ -2831,7 +2834,9 @@ again:
   	WARN_ON_ONCE(!dl_se);
   
   	if (dl_server(dl_se)) {
  +		this_cpu_write(dbg_in_dl_server_pick, true);	/* REPRO */
   		p = dl_se->server_pick_task(dl_se, rf);
  +		this_cpu_write(dbg_in_dl_server_pick, false);	/* REPRO */
   		if (!p) {
   			dl_server_stop(dl_se);
   			goto again;
  --- a/kernel/sched/fair.c
  +++ b/kernel/sched/fair.c
  @@ -21,6 +21,7 @@
    *  Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
    */
   #include <linux/energy_model.h>
  +#include <linux/delay.h>	/* REPRO */
   #include <linux/mmap_lock.h>
   #include <linux/jiffies.h>
   #include <linux/math.h>
  @@ -14849,6 +14850,11 @@ static inline bool nohz_idle_balance(str
   static inline void nohz_newidle_balance(struct rq *this_rq) { }
   #endif /* !CONFIG_NO_HZ_COMMON */
   
  +/* REPRO: fair.dbg_newidle_delay_us, see __pick_task_dl() */
  +DECLARE_PER_CPU(bool, dbg_in_dl_server_pick);
  +static unsigned int dbg_newidle_delay_us;
  +module_param(dbg_newidle_delay_us, uint, 0644);
  +
   /*
    * sched_balance_newidle is called by schedule() if this_cpu is about to become
    * idle. Attempts to pull tasks from other CPUs.
  @@ -14968,6 +14974,10 @@ static int sched_balance_newidle(struct
   			break;
   	}
   
  +	/* REPRO: widen the drop inside a dl server's pick, after the cost update */
  +	if (dbg_newidle_delay_us && this_cpu_read(dbg_in_dl_server_pick))
  +		udelay(dbg_newidle_delay_us);
  +
   	raw_spin_rq_lock(this_rq);
   
   	if (curr_cost > this_rq->max_idle_balance_cost)

  #!/bin/bash
  #
  # Reproducer for a stale rq->dl_server after a dl server's pick returns
  # RETRY_TASK. Needs a kernel with repro.patch applied, stress-ng and root.
  #
  # RT FIFO hogs starve the fair tasks, so the fair server runs them. The fair
  # tasks sleep often, so the server's pick finds no fair task and runs
  # sched_balance_newidle(), which drops the rq lock. An RT task woken from
  # another CPU in that window makes the pick return RETRY_TASK, and the
  # restarted pick hands the stale server to the RT task.
  #
  # usage: repro.sh [DURATION_S] [DELAY_US]
  
  DURATION=${1:-60}
  DELAY_US=${2:-500}
  KNOB=/sys/module/fair/parameters/dbg_newidle_delay_us
  N=$(nproc)
  
  [ -w $KNOB ] || { echo "$KNOB missing, is repro.patch applied?"; exit 1; }
  dmesg -C
  echo "$DELAY_US" > $KNOB
  
  # RT hogs on every CPU, RT pipe ping-pong across CPUs, fair tasks at 20% load
  stress-ng --cpu "$N" --sched fifo --sched-prio 10 --timeout "${DURATION}s" &> /dev/null &
  stress-ng --switch "$N" --sched fifo --sched-prio 50 --timeout "${DURATION}s" &> /dev/null &
  stress-ng --cpu $((N * 2)) --cpu-load 20 --timeout "${DURATION}s" &> /dev/null &
  stress-ng --switch "$N" --timeout "${DURATION}s" &> /dev/null &
  wait
  echo 0 > $KNOB
  
  dmesg | grep -E 'stale rq->dl_server|__pick_next_task: [0-9]+ callbacks suppressed'
  printed=$(dmesg | grep -c 'stale rq->dl_server')
  suppressed=$(dmesg | sed -n 's/.*__pick_next_task: \([0-9]*\) callbacks suppressed.*/\1/p' |
  	     awk '{ s += $1 } END { print s + 0 }')
  echo "stale rq->dl_server hand-offs: $((printed + suppressed))"

Thanks.

--
tejun

      parent reply	other threads:[~2026-10-31  1:19 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 22:18 Tejun Heo
2026-10-30 23:56 ` Tejun Heo
2026-10-31  1:19 ` Tejun Heo [this message]

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=6db076ee2562b5fb84d70ed59abe670e@kernel.org \
    --to=tj@kernel.org \
    --cc=bsegall@google.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=juri.lelli@redhat.com \
    --cc=kprateek.nayak@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mgorman@suse.de \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=rostedt@goodmis.org \
    --cc=vincent.guittot@linaro.org \
    --cc=vschneid@redhat.com \
    /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®