bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Tao Cui" <cui.tao@linux.dev>
Cc: sched-ext@lists.linux.dev, bpf@vger.kernel.org
Subject: Re: [PATCH 5/6] tools/sched_ext: scx_flatcg: Don't strand a cgroup on the ENQ_RACE path
Date: Thu, 12 Nov 2026 13:26:16 +0000	[thread overview]
Message-ID: <sashiko-outbox-211177@kernel.org> (raw)
In-Reply-To: <20261112130934.563321-6-cui.tao@linux.dev>

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] sched_ext: scx_flatcg: cgroup stranding in cgrp_enqueued()
  retry loop
- [Medium] sched_ext: scx_flatcg: 1024-iteration spin in cgrp_enqueued()
  after PNC_RACE
--

commit 183b4752d1564aee66142be30c8948ab1d3f9a90
Author: Tao Cui <cuitao@kylinos.cn>

tools/sched_ext: scx_flatcg: Don't strand a cgroup on the ENQ_RACE path

This commit adds a bounded retry loop in cgrp_enqueued() when
bpf_kptr_xchg(&stash->node, NULL) returns NULL, polling for either the
stashed cgv_node or a non-empty cgroup DSQ before returning.

> diff --git a/tools/sched_ext/scx_flatcg.bpf.c b/tools/sched_ext/scx_flatcg.bpf.c
> index 1d168d492203c..5f2b9d0b867a7 100644
> --- a/tools/sched_ext/scx_flatcg.bpf.c
> +++ b/tools/sched_ext/scx_flatcg.bpf.c
> @@ -285,11 +285,24 @@ static void cgrp_enqueued(struct cgroup *cgrp, struct fcg_cgrp_ctx *cgc)
>  		return;
>  	}
>  
> -	/* NULL if the node is already on the rbtree */
> +	/* NULL if the node is on the rbtree or in the picker's hands */
>  	cgv_node = bpf_kptr_xchg(&stash->node, NULL);
>  	if (!cgv_node) {
> +		/*
> +		 * The node is either back on the rbtree or in the
> +		 * picker's hands; bailing out strands the cgroup. Wait
> +		 * for the picker to resolve and retry.
> +		 */
>  		stat_inc(FCG_STAT_ENQ_RACE);
> -		return;
> +		bpf_repeat(CGROUP_MAX_RETRIES) {
> +			cgv_node = bpf_kptr_xchg(&stash->node, NULL);
> +			if (cgv_node)
> +				break;
> +			if (scx_bpf_dsq_nr_queued(cgid) > 0)
> +				break;
> +		}

[Severity: Medium]
Can breaking out of the retry loop on scx_bpf_dsq_nr_queued(cgid) > 0
still strand the cgroup when try_pick_next_cgroup() has already checked
scx_bpf_dsq_nr_queued(cgid) or when the queued task is consumed before it
does?

In try_pick_next_cgroup(), clearing cgc->queued, checking
scx_bpf_dsq_nr_queued(cgid), and stashing cgv_node are not atomic:

tools/sched_ext/scx_flatcg.bpf.c:try_pick_next_cgroup() {
    ...
	__sync_val_compare_and_swap(&cgc->queued, 1, 0);

	if (scx_bpf_dsq_nr_queued(cgid)) {
		bpf_spin_lock(&cgv_tree_lock);
		bpf_rbtree_add(&cgv_tree, &cgv_node->rb_node, cgv_node_less);
		bpf_spin_unlock(&cgv_tree_lock);
		stat_inc(FCG_STAT_PNC_RACE);
	} else {
		cgv_node = bpf_kptr_xchg(&stash->node, cgv_node);
    ...
}

If try_pick_next_cgroup() observes an empty DSQ before a second enqueue
completes its deferred DSQ insert, cgrp_enqueued() sees the second task in
the DSQ and bails out while the picker proceeds to stash cgv_node:

CPU0                       CPU1                         CPU2
try_pick_next_cgroup()
  cmpxchg(&cgc->queued,
          1, 0)
  dsq_nr_queued(cgid) == 0
                           fcg_enqueue(T0)
                             cgrp_enqueued()
                               cmpxchg(&cgc->queued,
                                       0, 1)
                               kptr_xchg(&stash->node,
                                         NULL) == NULL
                                                        fcg_enqueue(T1)
                                                          cgrp_enqueued()
                                                            cgc->queued == 1
                                                        <T1 inserted in DSQ>
                               dsq_nr_queued(cgid) > 0
                               return;
  kptr_xchg(&stash->node,
            cgv_node)  <-- cgroup stranded

The same stranding happens if T1 is already in the DSQ when CPU1 checks
scx_bpf_dsq_nr_queued(cgid), and another CPU with an active slice for
cgid consumes T1 via scx_bpf_dsq_move_to_local() in fcg_dispatch() before
CPU0 reaches its scx_bpf_dsq_nr_queued(cgid) check.  In both cases
cgv_node ends up in stash->node with cgc->queued left at 1 and tasks in
the DSQ, so subsequent enqueues hit FCG_STAT_ENQ_SKIP and the watchdog
terminates the scheduler with SCX_EXIT_ERROR_STALL.

> +		if (!cgv_node)
> +			return;
>  	}

[Severity: Medium]
Does this loop spin through all CGROUP_MAX_RETRIES (1024) iterations
whenever cgv_node is already on cgv_tree with cgc->queued == 0 and an
empty DSQ?

When try_pick_next_cgroup() hits FCG_STAT_PNC_RACE, it clears cgc->queued
to 0 and re-adds cgv_node to cgv_tree without setting cgc->queued back to
1.  Once the queued tasks in that DSQ are consumed, cgv_node remains on
cgv_tree (so stash->node is NULL), the DSQ is empty, and cgc->queued is 0.
On the next fcg_enqueue() for this cgroup, cgrp_enqueued() sets
cgc->queued to 1 and enters the retry loop, but because the current task's
DSQ insertion is deferred until fcg_enqueue() returns, neither
bpf_kptr_xchg(&stash->node, NULL) nor scx_bpf_dsq_nr_queued(cgid) > 0 can
succeed, burning 1024 atomic exchanges and kfunc calls under the rq lock
and widening the window for a concurrent picker to pop and stash cgv_node
after the retries expire.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261112130934.563321-1-cui.tao@linux.dev?part=5

  reply	other threads:[~2026-11-12 13:26 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 13:09 [PATCH 0/6] sched_ext: fix kfunc, sub-sched, flatcg and idle-mask issues Tao Cui
2026-11-12 13:09 ` [PATCH 1/6] sched_ext: Reject DSQ iterator kfuncs in tracing programs Tao Cui
2026-11-12 17:34   ` Tejun Heo
2026-11-12 13:09 ` [PATCH 2/6] sched_ext: Make scx_bpf_cid_node() a benign lookup for invalid cids Tao Cui
2026-11-12 17:34   ` Tejun Heo
2026-11-12 13:09 ` [PATCH 3/6] sched_ext: Return -EBUSY from a pre-init scx_bpf_sub_caps() read Tao Cui
2026-11-12 17:34   ` Tejun Heo
2026-11-12 13:09 ` [PATCH 4/6] sched_ext: Reject cid_online/cid_offline on sub-schedulers Tao Cui
2026-11-12 17:34   ` Tejun Heo
2026-11-12 13:09 ` [PATCH 5/6] tools/sched_ext: scx_flatcg: Don't strand a cgroup on the ENQ_RACE path Tao Cui
2026-11-12 13:26   ` sashiko-bot [this message]
2026-11-12 17:34   ` Tejun Heo
2026-11-12 13:09 ` [PATCH 6/6] sched_ext: Clear the built-in idle masks on CPU hotplug transitions Tao Cui
2026-11-12 13:32   ` sashiko-bot
2026-11-12 17:34   ` Tejun Heo
2026-11-12 17:34 ` [PATCH 0/6] sched_ext: fix kfunc, sub-sched, flatcg and idle-mask issues Tejun Heo

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=sashiko-outbox-211177@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=cui.tao@linux.dev \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=sched-ext@lists.linux.dev \
    /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;
as well as URLs for NNTP newsgroup(s).