Sched_ext development
 help / color / mirror / Atom feed
From: Andrea Righi <arighi@nvidia.com>
To: Emil Tsalapatis <etsal@meta.com>
Cc: tj@kernel.org, void@manifault.com, changwoo@igalia.com,
	sched-ext@lists.linux.dev, Jakub Kicinski <kuba@kernel.org>,
	"Emil Tsalapatis (Meta)" <emil@etsalapatis.com>
Subject: Re: [PATCH] sched_ext: defer queue_balance_callback() until after ops.dispatch
Date: Thu, 9 Oct 2025 21:36:38 +0200	[thread overview]
Message-ID: <aOgOxtHCeyRT_7jn@gpd4> (raw)
In-Reply-To: <20251009173620.1882642-1-etsal@meta.com>

Hi Emil,

On Thu, Oct 09, 2025 at 10:36:20AM -0700, Emil Tsalapatis wrote:
> The sched_ext code calls queue_balance_callback() during .balance(),

I think we call queue_balance_callback() from .enqueue_task(), right?

> to defer operations that drop multiple locks until we can unpin them.
> The is call assumes that the rq lock is held until the callbacks are

s/is//

> invoked, and the pending callbacks will not be visible to any other
> threads. This is enforced by a WARN_ON_ONCE() in rq_pin_lock().
> 
> However, balance_one() may actually drop the lock during a BPF dispatch
> call. Another thread may win the race to get the rq lock and see the
> pending callback. To avoid this, sched_ext must only queue the callback
> after the dispatch calls have completed.
> 
> CPU 0                   CPU 1           CPU 2
> 
> scx_balance()
>   rq_unpin_lock()
>   scx_balance_one()
>     |= IN_BALANCE	scx_enqueue()
>     ops.dispatch()
>       rq_unlock()
>                         rq_lock()
>                         queue_balance_callback()
>                         rq_unlock()
>                                         [WARN] rq_pin_lock()
>       rq_lock()
>     &= ~IN_BALANCE
> rq_repin_lock()
> 
> Reported-by: Jakub Kicinski <kuba@kernel.org> 
> Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com>
> ---
>  kernel/sched/ext.c   | 28 ++++++++++++++++++++++++++--
>  kernel/sched/sched.h |  1 +
>  2 files changed, 27 insertions(+), 2 deletions(-)
> 
> diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
> index 5f957cff5d17..35dfce06297a 100644
> --- a/kernel/sched/ext.c
> +++ b/kernel/sched/ext.c
> @@ -780,13 +780,23 @@ static void schedule_deferred(struct rq *rq)
>  	if (rq->scx.flags & SCX_RQ_IN_WAKEUP)
>  		return;
>  
> +	/* Don't do anything if there already is a deferred operation. */
> +	if (rq->scx.flags & SCX_RQ_BAL_PENDING)
> +		return;
> +
>  	/*
>  	 * If in balance, the balance callbacks will be called before rq lock is
>  	 * released. Schedule one.
> +	 *
> +	 *
> +	 * We can't directly insert the callback into the
> +	 * rq's list: The call can drop its lock and make the pending balance
> +	 * callback visible to unrelated code paths that call rq_pin_lock().
> +	 *
> +	 * Just let balance_one() know that it must do it itself.
>  	 */
>  	if (rq->scx.flags & SCX_RQ_IN_BALANCE) {
> -		queue_balance_callback(rq, &rq->scx.deferred_bal_cb,
> -				       deferred_bal_cb_workfn);
> +		rq->scx.flags |= SCX_RQ_BAL_CB_PENDING;
>  		return;
>  	}
>  
> @@ -2003,6 +2013,18 @@ static void flush_dispatch_buf(struct scx_sched *sch, struct rq *rq)
>  	dspc->cursor = 0;
>  }
>  
> +static inline void maybe_queue_balance_callback(struct rq *rq)
> +{
> +	lockdep_assert_rq_held(rq);
> +
> +	if (rq->scx.flags & SCX_RQ_BAL_CB_PENDING) {
> +		queue_balance_callback(rq, &rq->scx.deferred_bal_cb,
> +				       deferred_bal_cb_workfn);
> +	}
> +
> +	rq->scx.flags &= SCX_RQ_BAL_CB_PENDING;

Hm... this looks wrong. I think you want to clear SCX_RQ_BAL_CB_PENDING, so
it should be:

	rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING;

And while at it, just to better reflect the logic:

	if (rq->scx.flags & SCX_RQ_BAL_CB_PENDING) {
		queue_balance_callback(rq, &rq->scx.deferred_bal_cb,
				       deferred_bal_cb_workfn);
		rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING;
	}

> +}
> +
>  static int balance_one(struct rq *rq, struct task_struct *prev)
>  {
>  	struct scx_sched *sch = scx_root;
> @@ -2150,6 +2172,8 @@ static int balance_scx(struct rq *rq, struct task_struct *prev,
>  #endif
>  	rq_repin_lock(rq, rf);
>  
> +	maybe_queue_balance_callback(rq);
> +
>  	return ret;
>  }
>  
> diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
> index be9745d104f7..8f3935785fc5 100644
> --- a/kernel/sched/sched.h
> +++ b/kernel/sched/sched.h
> @@ -757,6 +757,7 @@ enum scx_rq_flags {
>  	SCX_RQ_BAL_KEEP		= 1 << 3, /* balance decided to keep current */
>  	SCX_RQ_BYPASSING	= 1 << 4,
>  	SCX_RQ_CLK_VALID	= 1 << 5, /* RQ clock is fresh and valid */
> +	SCX_RQ_BAL_CB_PENDING	= 1 << 6, /* must queue a cb after dispatching */
>  
>  	SCX_RQ_IN_WAKEUP	= 1 << 16,
>  	SCX_RQ_IN_BALANCE	= 1 << 17,
> -- 
> 2.47.3
> 

Thanks,
-Andrea

  reply	other threads:[~2025-10-09 19:36 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-09 17:36 [PATCH] sched_ext: defer queue_balance_callback() until after ops.dispatch Emil Tsalapatis
2025-10-09 19:36 ` Andrea Righi [this message]
2025-10-09 21:53   ` Emil Tsalapatis
2025-10-09 21:57     ` Andrea Righi

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=aOgOxtHCeyRT_7jn@gpd4 \
    --to=arighi@nvidia.com \
    --cc=changwoo@igalia.com \
    --cc=emil@etsalapatis.com \
    --cc=etsal@meta.com \
    --cc=kuba@kernel.org \
    --cc=sched-ext@lists.linux.dev \
    --cc=tj@kernel.org \
    --cc=void@manifault.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