]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
sched_ext: Pin parent scx_sched across a child sub-scheduler's lifetime
authorTejun Heo <tj@kernel.org>
Thu, 25 Jun 2026 23:23:56 +0000 (13:23 -1000)
committerTejun Heo <tj@kernel.org>
Mon, 29 Jun 2026 20:10:29 +0000 (10:10 -1000)
A child sub-scheduler dereferences its parent scx_sched throughout its life,
e.g., in scx_sub_disable() which reparents the child's tasks and calls
parent->ops.sub_detach() after unlinking from the parent. However, the
parent is pinned only through parent->sub_kset, which is dropped during
disable. The parent scx_sched can be RCU-freed while a child is still
disabling.

Take a direct reference on the parent in scx_alloc_and_add_sched(), dropped
in scx_sched_free_rcu_work(), so a parent always outlives its descendants.

Signed-off-by: Tejun Heo <tj@kernel.org>
kernel/sched/ext/ext.c

index aecbb021d6d71559973d9aabea161e965f569b96..3b2e13bc924bcce1c17cb71bd639d07a448f55a7 100644 (file)
@@ -4916,6 +4916,8 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
                cgroup_put(sch_cgroup(sch));
        if (sch->sub_kset)
                kobject_put(&sch->sub_kset->kobj);
+       if (scx_parent(sch))
+               kobject_put(&scx_parent(sch)->kobj);
 #endif /* CONFIG_EXT_SUB_SCHED */
 
        for_each_possible_cpu(cpu) {
@@ -6863,12 +6865,19 @@ static struct scx_sched *scx_alloc_and_add_sched(struct scx_enable_cmd *cmd,
        INIT_LIST_HEAD(&sch->children);
        INIT_LIST_HEAD(&sch->sibling);
 
-       if (parent)
+       if (parent) {
+               /*
+                * Pin @parent for @sch's lifetime. The kobject hierarchy pins
+                * it only via @parent->sub_kset, which is dropped during
+                * disable. Released in scx_sched_free_rcu_work().
+                */
+               kobject_get(&parent->kobj);
                ret = kobject_init_and_add(&sch->kobj, &scx_ktype,
                                           &parent->sub_kset->kobj,
                                           "sub-%llu", cgroup_id(cgrp));
-       else
+       } else {
                ret = kobject_init_and_add(&sch->kobj, &scx_ktype, NULL, "root");
+       }
 
        if (ret < 0) {
                RCU_INIT_POINTER(ops->priv, NULL);