The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Tejun Heo <tj@kernel.org>
To: void@manifault.com, arighi@nvidia.com, multics69@gmail.com
Cc: linux-kernel@vger.kernel.org, sched-ext@lists.linux.dev,
	memxor@gmail.com, bpf@vger.kernel.org, Tejun Heo <tj@kernel.org>
Subject: [PATCH 16/46] sched_ext: Implement cgroup subtree iteration for scx_task_iter
Date: Fri, 19 Sep 2025 14:58:39 -1000	[thread overview]
Message-ID: <20250920005931.2753828-17-tj@kernel.org> (raw)
In-Reply-To: <20250920005931.2753828-1-tj@kernel.org>

For the planned cgroup sub-scheduler support, enable/disable operations are
going to be subtree specific and iterating all tasks in the system for those
operations can be unnecessarily expensive and disruptive.

cgroup already has mechanisms to perform subtree task iterations. Implement
cgroup subtree iteration for scx_task_iter:

- Add optional @cgrp to scx_task_iter_start() which enables cgroup subtree
  iteration.

- Make scx_task_iter use combination of css_next_descendant_pre() and
  css_task_iter to iterate all live tasks for cgroup iterations.

- After live task iteration is finished, scan scx_dying_tasks and only visit
  tasks that are in the cgroup subtree. As scx_dying_tasks is most likely
  really short, this should be pretty cheap.

Signed-off-by: Tejun Heo <tj@kernel.org>
---
 kernel/sched/ext.c | 76 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 67 insertions(+), 9 deletions(-)

diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c
index 6ae9ee5b9a50..ca8221378924 100644
--- a/kernel/sched/ext.c
+++ b/kernel/sched/ext.c
@@ -451,11 +451,17 @@ struct scx_task_iter {
 	struct rq_flags			rf;
 	u32				cnt;
 	bool				list_locked;
+#ifdef CONFIG_CGROUPS
+	struct cgroup			*cgrp;
+	struct cgroup_subsys_state	*css_pos;
+	struct css_task_iter		css_iter;
+#endif
 };
 
 /**
  * scx_task_iter_start - Lock scx_tasks_lock and start a task iteration
  * @iter: iterator to init
+ * @cgrp: Optional root of cgroup subhierarchy to iterate
  *
  * Initialize @iter and return with scx_tasks_lock held. Once initialized, @iter
  * must eventually be stopped with scx_task_iter_stop().
@@ -469,8 +475,14 @@ struct scx_task_iter {
  * All tasks which existed when the iteration started are guaranteed to be
  * visited as long as they still exist. Tasks which exit while iteration is in
  * progress may be visited twice. The caller must be able to handle such cases.
+ *
+ * @if @cgrp is NULL, scx_live_tasks are walked followed by scx_dying_tasks. If
+ * @cgrp is not NULL, @cgrp's tasks are walked using css_task_iter followed by
+ * scx_dying_tasks. To guarantee that all tasks are visited at least once, the
+ * caller must be holding scx_fork_rwsem. In the cgroup case, the caller must
+ * also be holding scx_cgroup_rwsem to prevent cgroup task migrations.
  */
-static void scx_task_iter_start(struct scx_task_iter *iter)
+static void scx_task_iter_start(struct scx_task_iter *iter, struct cgroup *cgrp)
 {
 	BUILD_BUG_ON(__SCX_DSQ_ITER_ALL_FLAGS &
 		     ((1U << __SCX_DSQ_LNODE_PRIV_SHIFT) - 1));
@@ -478,8 +490,20 @@ static void scx_task_iter_start(struct scx_task_iter *iter)
 	spin_lock_irq(&scx_tasks_lock);
 
 	iter->head = &scx_live_tasks;
+#ifdef CONFIG_CGROUPS
+	if (cgrp) {
+		iter->cgrp = cgrp;
+		iter->css_pos = css_next_descendant_pre(NULL, &iter->cgrp->self);
+		css_task_iter_start(iter->css_pos, 0, &iter->css_iter);
+		/* walking cgroup tasks instead, skip scx_live_tasks */
+		iter->head = &scx_dying_tasks;
+	} else {
+		iter->cgrp = NULL;
+		iter->css_pos = NULL;
+	}
+#endif
 	iter->cursor = (struct sched_ext_entity){ .flags = SCX_TASK_CURSOR };
-	list_add(&iter->cursor.tasks_node, &scx_live_tasks);
+	list_add(&iter->cursor.tasks_node, iter->head);
 	iter->locked_task = NULL;
 	iter->cnt = 0;
 	iter->list_locked = true;
@@ -530,6 +554,8 @@ static void __scx_task_iter_maybe_relock(struct scx_task_iter *iter)
 static void scx_task_iter_stop(struct scx_task_iter *iter)
 {
 	__scx_task_iter_maybe_relock(iter);
+	if (iter->css_pos)
+		css_task_iter_end(&iter->css_iter);
 	list_del_init(&iter->cursor.tasks_node);
 	scx_task_iter_unlock(iter);
 }
@@ -557,13 +583,45 @@ static struct task_struct *scx_task_iter_next(struct scx_task_iter *iter)
 		__scx_task_iter_maybe_relock(iter);
 	}
 retry:
+
+#ifdef CONFIG_CGROUPS
+	/*
+	 * For cgroup iterations, use css_task_iter for live tasks. iter->head
+	 * is already set to scx_dying_tasks.
+	 */
+	while (iter->css_pos) {
+		struct task_struct *p;
+
+		p = css_task_iter_next(&iter->css_iter);
+		if (p)
+			return p;
+
+		css_task_iter_end(&iter->css_iter);
+		iter->css_pos = css_next_descendant_pre(iter->css_pos,
+							&iter->cgrp->self);
+		if (iter->css_pos)
+			css_task_iter_start(iter->css_pos, 0, &iter->css_iter);
+	}
+#endif
+
 	list_for_each_entry(pos, cursor, tasks_node) {
+		struct task_struct *p = container_of(pos, struct task_struct, scx);
+
 		if (&pos->tasks_node == iter->head)
 			break;
-		if (!(pos->flags & SCX_TASK_CURSOR)) {
-			list_move(cursor, &pos->tasks_node);
-			return container_of(pos, struct task_struct, scx);
-		}
+		if (pos->flags & SCX_TASK_CURSOR)
+			continue;
+#ifdef CONFIG_CGROUPS
+		/*
+		 * For cgroup iterations, this loop is only used for iterating
+		 * dying tasks. Filter out tasks which aren't in the target
+		 * subtree.
+		 */
+		if (iter->cgrp && !task_under_cgroup_hierarchy(p, iter->cgrp))
+			continue;
+#endif
+		list_move(cursor, &pos->tasks_node);
+		return p;
 	}
 
 	if (iter->head == &scx_live_tasks) {
@@ -3936,7 +3994,7 @@ static void scx_disable_workfn(struct kthread_work *work)
 
 	scx_init_task_enabled = false;
 
-	scx_task_iter_start(&sti);
+	scx_task_iter_start(&sti, NULL);
 	while ((p = scx_task_iter_next_locked(&sti))) {
 		/* @p may be being visited twice, doesn't matter */
 		const struct sched_class *old_class = p->sched_class;
@@ -4639,7 +4697,7 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)
 	if (ret)
 		goto err_disable_unlock_all;
 
-	scx_task_iter_start(&sti);
+	scx_task_iter_start(&sti, NULL);
 	while ((p = scx_task_iter_next_locked(&sti))) {
 		/*
 		 * Task iteration may visit the same task twice when racing
@@ -4688,7 +4746,7 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)
 	 * scx_tasks_lock.
 	 */
 	percpu_down_write(&scx_fork_rwsem);
-	scx_task_iter_start(&sti);
+	scx_task_iter_start(&sti, NULL);
 	while ((p = scx_task_iter_next_locked(&sti))) {
 		/* @p may be being visited twice, doesn't matter */
 		const struct sched_class *old_class = p->sched_class;
-- 
2.51.0


  parent reply	other threads:[~2025-09-20  0:59 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-20  0:58 [PATCHSET RFC] sched_ext: Implement cgroup sub-scheduler support Tejun Heo
2025-09-20  0:58 ` [PATCH 01/46] sched_ext: Use rhashtable_lookup() instead of rhashtable_lookup_fast() Tejun Heo
2025-09-20  0:58 ` [PATCH 02/46] sched_ext: Improve SCX_KF_DISPATCH comment Tejun Heo
2025-09-20  0:58 ` [PATCH 03/46] sched_ext: Fix stray scx_root usage in task_can_run_on_remote_rq() Tejun Heo
2025-09-20  0:58 ` [PATCH 04/46] sched_ext: Use bitfields for boolean warning flags Tejun Heo
2025-09-20  0:58 ` [PATCH 05/46] sched_ext: Add SCX_EFLAG_INITIALIZED to indicate successful ops.init() Tejun Heo
2025-09-20  0:58 ` [PATCH 06/46] sched_ext: Make qmap dump operation non-destructive Tejun Heo
2025-09-20  0:58 ` [PATCH 07/46] tools/sched_ext: scx_qmap: Make debug output quieter by default Tejun Heo
2025-09-20  0:58 ` [PATCH 08/46] sched_ext: Separate out scx_kick_cpu() and add @sch to it Tejun Heo
2025-09-20  0:58 ` [PATCH 09/46] sched_ext: Add the @sch parameter to __bstr_format() Tejun Heo
2025-09-20  0:58 ` [PATCH 10/46] sched_ext: Add the @sch parameter to ext_idle helpers Tejun Heo
2025-09-20  0:58 ` [PATCH 11/46] sched_ext: Drop kf_cpu_valid() Tejun Heo
2025-09-20  0:58 ` [PATCH 12/46] sched_ext: Add the @sch parameter to scx_dsq_insert_preamble/commit() Tejun Heo
2025-09-20  0:58 ` [PATCH 13/46] sched_ext: Drop scx_kf_exit() and scx_kf_error() Tejun Heo
2025-09-20  0:58 ` [PATCH 14/46] sched_ext: Misc updates around scx_sched instance pointer Tejun Heo
2025-09-20  0:58 ` [PATCH 15/46] sched_ext: Keep dying tasks on a separate list Tejun Heo
2025-09-20  0:58 ` Tejun Heo [this message]
2025-09-20  0:58 ` [PATCH 17/46] sched_ext: Add @kargs to scx_fork() Tejun Heo
2025-09-20  0:58 ` [PATCH 18/46] sched/core: Swap the order between sched_post_fork() and cgroup_post_fork() Tejun Heo
2025-09-20  0:58 ` [PATCH 19/46] cgroup: Expose some cgroup helpers Tejun Heo
2025-09-20  0:58 ` [PATCH 20/46] sched_ext: Update p->scx.disallow warning in scx_init_task() Tejun Heo
2025-09-20  0:58 ` [PATCH 21/46] sched_ext: Minor reorganization of enable/disable path Tejun Heo
2025-09-20  0:58 ` [PATCH 22/46] sched_ext: Factor out scx_claim_exit() from scx_disable() Tejun Heo
2025-09-20  0:58 ` [PATCH 23/46] sched_ext: Introduce cgroup sub-sched support Tejun Heo
2025-09-20  0:58 ` [PATCH 24/46] HACK_NOT_FOR_UPSTREAM: BPF: Implement prog grouping hack Tejun Heo
2025-09-20  0:58 ` [PATCH 25/46] sched_ext: Introduce scx_task_sched[_rcu]() Tejun Heo
2025-09-20  0:58 ` [PATCH 26/46] sched_ext: Introduce scx_prog_sched() Tejun Heo
2025-09-20  0:58 ` [PATCH 27/46] sched_ext: Ignore insertions of not-owned tasks into DSQs Tejun Heo
2025-09-20  0:58 ` [PATCH 28/46] sched_ext: scx_dsq_move() should validate the task belongs to the right scheduler Tejun Heo
2025-09-20  0:58 ` [PATCH 29/46] sched_ext: Refactor task init/exit helpers Tejun Heo
2025-09-20  0:58 ` [PATCH 30/46] sched_ext: Make scx_prio_less() handle multiple schedulers Tejun Heo
2025-09-20  0:58 ` [PATCH 31/46] sched_ext: Move bypass_depth into scx_sched Tejun Heo
2025-09-20  0:58 ` [PATCH 32/46] sched_ext: Make bypass mode sub-sched aware Tejun Heo
2025-09-20  0:58 ` [PATCH 33/46] sched_ext: Factor out scx_dispatch_sched() Tejun Heo
2025-09-20  0:58 ` [PATCH 34/46] sched_ext: When calling ops.dispatch() @prev must be on the same scx_sched Tejun Heo
2025-09-20  0:58 ` [PATCH 35/46] sched_ext: Dispatch from all scx_sched instances Tejun Heo
2025-09-20  0:58 ` [PATCH 36/46] sched_ext: Move scx_dsp_ctx and scx_dsp_max_batch into scx_sched Tejun Heo
2025-09-20  0:59 ` [PATCH 37/46] sched_ext: Make watchdog sub-sched aware Tejun Heo
2025-09-20  0:59 ` [PATCH 38/46] sched_ext: Convert scx_dump_state() spinlock to raw spinlock Tejun Heo
2025-09-20  0:59 ` [PATCH 39/46] sched_ext: Support dumping multiple schedulers and add scheduler identification Tejun Heo
2025-09-20  0:59 ` [PATCH 40/46] sched_ext: Implement cgroup sub-sched enabling and disabling Tejun Heo
2025-09-20  0:59 ` [PATCH 41/46] HACK_NOT_FOR_UPSTREAM: sched_ext: Work around @aux__prog prototype mismatch Tejun Heo
2025-09-20  0:59 ` [PATCH 42/46] sched_ext: Wrap global DSQs in per-node structure Tejun Heo
2025-09-20  0:59 ` [PATCH 43/46] sched_ext: Add bypass DSQ for sub-schedulers Tejun Heo
2025-09-20  0:59 ` [PATCH 44/46] sched_ext: Factor out scx_link_sched() and scx_unlink_sched() Tejun Heo
2025-09-20  0:59 ` [PATCH 45/46] sched_ext: Add rhashtable lookup for sub-schedulers Tejun Heo
2025-09-20  0:59 ` [PATCH 46/46] sched_ext: Add basic building blocks for nested sub-scheduler dispatching 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=20250920005931.2753828-17-tj@kernel.org \
    --to=tj@kernel.org \
    --cc=arighi@nvidia.com \
    --cc=bpf@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=memxor@gmail.com \
    --cc=multics69@gmail.com \
    --cc=sched-ext@lists.linux.dev \
    --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