net/sched: sch_qfq: Avoid triggering might_sleep in atomic context in qfq_delete_class
authorXiang Mei <xmei5@asu.edu>
Thu, 17 Jul 2025 23:01:28 +0000 (16:01 -0700)
committerSalvatore Bonaccorso <carnil@debian.org>
Sat, 2 Aug 2025 13:13:02 +0000 (15:13 +0200)
Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git/commit?id=29c13f40a43118ba50606be564f772bc6d5819b0

[ Upstream commit cf074eca0065bc5142e6004ae236bb35a2687fdf ]

might_sleep could be trigger in the atomic context in qfq_delete_class.

qfq_destroy_class was moved into atomic context locked
by sch_tree_lock to avoid a race condition bug on
qfq_aggregate. However, might_sleep could be triggered by
qfq_destroy_class, which introduced sleeping in atomic context (path:
qfq_destroy_class->qdisc_put->__qdisc_destroy->lockdep_unregister_key
->might_sleep).

Considering the race is on the qfq_aggregate objects, keeping
qfq_rm_from_agg in the lock but moving the left part out can solve
this issue.

Fixes: 5e28d5a3f774 ("net/sched: sch_qfq: Fix race condition on qfq_aggregate")
Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
Signed-off-by: Xiang Mei <xmei5@asu.edu>
Link: https://patch.msgid.link/4a04e0cc-a64b-44e7-9213-2880ed641d77@sabinyo.mountain
Reviewed-by: Cong Wang <xiyou.wangcong@gmail.com>
Reviewed-by: Dan Carpenter <dan.carpenter@linaro.org>
Link: https://patch.msgid.link/20250717230128.159766-1-xmei5@asu.edu
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Gbp-Pq: Topic bugfix/all
Gbp-Pq: Name net-sched-sch_qfq-Avoid-triggering-might_sleep-in-at.patch

net/sched/sch_qfq.c

index f2692c9173f79c04f410b2e70c6f4626ff6b789b..2f2863ae18ad596fd553e7ae4658f556248fe3d2 100644 (file)
@@ -540,9 +540,6 @@ destroy_class:
 
 static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl)
 {
-       struct qfq_sched *q = qdisc_priv(sch);
-
-       qfq_rm_from_agg(q, cl);
        gen_kill_estimator(&cl->rate_est);
        qdisc_put(cl->qdisc);
        kfree(cl);
@@ -561,10 +558,11 @@ static int qfq_delete_class(struct Qdisc *sch, unsigned long arg,
 
        qdisc_purge_queue(cl->qdisc);
        qdisc_class_hash_remove(&q->clhash, &cl->common);
-       qfq_destroy_class(sch, cl);
+       qfq_rm_from_agg(q, cl);
 
        sch_tree_unlock(sch);
 
+       qfq_destroy_class(sch, cl);
        return 0;
 }
 
@@ -1505,6 +1503,7 @@ static void qfq_destroy_qdisc(struct Qdisc *sch)
        for (i = 0; i < q->clhash.hashsize; i++) {
                hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i],
                                          common.hnode) {
+                       qfq_rm_from_agg(q, cl);
                        qfq_destroy_class(sch, cl);
                }
        }