]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net/sched: dualpi2: fix GSO backlog accounting
authorXingquan Liu <b1n@b1n.io>
Fri, 19 Jun 2026 15:13:47 +0000 (11:13 -0400)
committerJakub Kicinski <kuba@kernel.org>
Sun, 21 Jun 2026 22:16:03 +0000 (15:16 -0700)
When DualPI2 splits a GSO skb into N segments, it propagates N
additional packets to its parent before returning NET_XMIT_SUCCESS.
The parent then accounts for the original skb once more, leaving its
qlen one larger than the number of packets actually queued.

With QFQ as the parent, after all real packets are dequeued, QFQ still
has a non-zero qlen while its in-service aggregate has no active
classes. qfq_choose_next_agg() returns NULL and qfq_dequeue() passes
the result to qfq_peek_skb(), causing a NULL pointer dereference.

Follow the same pattern used by tbf_segment() and taprio: count only
successfully queued segments, propagate the difference between the
original skb and those segments, and return NET_XMIT_SUCCESS whenever
at least one segment was queued.

Fixes: 8f9516daedd6 ("sched: Add enqueue/dequeue of dualpi2 qdisc")
Cc: stable@vger.kernel.org
Signed-off-by: Xingquan Liu <b1n@b1n.io>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Reviewed-by: Victor Nogueira <victor@mojatatu.com>
Link: https://patch.msgid.link/20260619151447.223640-1-b1n@b1n.io
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/sched/sch_dualpi2.c

index d7c3254ef800f8de39783706baff42bb8c54f82a..5434df6ca8efe600cdec5eef2b05c01fef54bd89 100644 (file)
@@ -461,7 +461,7 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
                if (IS_ERR_OR_NULL(nskb))
                        return qdisc_drop(skb, sch, to_free);
 
-               cnt = 1;
+               cnt = 0;
                byte_len = 0;
                orig_len = qdisc_pkt_len(skb);
                skb_list_walk_safe(nskb, nskb, next) {
@@ -488,16 +488,15 @@ static int dualpi2_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
                                byte_len += nskb->len;
                        }
                }
-               if (cnt > 1) {
+               if (cnt > 0) {
                        /* The caller will add the original skb stats to its
                         * backlog, compensate this if any nskb is enqueued.
                         */
-                       --cnt;
-                       byte_len -= orig_len;
+                       qdisc_tree_reduce_backlog(sch, 1 - cnt,
+                                                 orig_len - byte_len);
                }
-               qdisc_tree_reduce_backlog(sch, -cnt, -byte_len);
                consume_skb(skb);
-               return err;
+               return cnt > 0 ? NET_XMIT_SUCCESS : err;
        }
        return dualpi2_enqueue_skb(skb, sch, to_free);
 }