On 4/13/26 6:37 PM, [email protected] wrote:
> From: Chia-Yu Chang <[email protected]>
> 
> Fix dualpi2_change() to correctly enforce updated limit and memlimit values
> after a configuration change of the dualpi2 qdisc.
> 
> Before this patch, dualpi2_change() always attempted to dequeue packets via
> the root qdisc (C-queue) when reducing backlog or memory usage, and
> unconditionally assumed that a valid skb will be returned. When traffic
> classification results in packets being queued in the L-queue while the
> C-queue is empty, this leads to a NULL skb dereference during limit or
> memlimit enforcement.
> 
> This is fixed by first dequeuing from the C-queue path if it is non-empty.
> Once the C-queue is empty, packets are dequeued directly from the L-queue.
> Return values from qdisc_dequeue_internal() are checked for both queues. When
> dequeuing from the L-queue, the parent qdisc qlen and backlog counters are
> updated explicitly to keep overall qdisc statistics consistent.
> 
> Fixes: 320d031ad6e4 ("sched: Struct definition and parsing of dualpi2 qdisc")
> Signed-off-by: Chia-Yu Chang <[email protected]>
> ---
>  net/sched/sch_dualpi2.c | 24 +++++++++++++++++++-----
>  1 file changed, 19 insertions(+), 5 deletions(-)
> 
> diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c
> index 6d7e6389758d..56d4422970b6 100644
> --- a/net/sched/sch_dualpi2.c
> +++ b/net/sched/sch_dualpi2.c
> @@ -872,11 +872,25 @@ static int dualpi2_change(struct Qdisc *sch, struct 
> nlattr *opt,
>       old_backlog = sch->qstats.backlog;
>       while (qdisc_qlen(sch) > sch->limit ||
>              q->memory_used > q->memory_limit) {
> -             struct sk_buff *skb = qdisc_dequeue_internal(sch, true);
> -
> -             q->memory_used -= skb->truesize;
> -             qdisc_qstats_backlog_dec(sch, skb);
> -             rtnl_qdisc_drop(skb, sch);
> +             int c_len = qdisc_qlen(sch) - qdisc_qlen(q->l_queue);
> +             struct sk_buff *skb = NULL;
> +
> +             if (c_len) {
> +                     skb = qdisc_dequeue_internal(sch, true);
> +                     if (!skb)
> +                             break;
> +                     q->memory_used -= skb->truesize;
> +                     rtnl_qdisc_drop(skb, sch);
> +             } else if (qdisc_qlen(q->l_queue)) {
> +                     skb = qdisc_dequeue_internal(q->l_queue, true);
> +                     if (!skb)
> +                             break;
> +                     q->memory_used -= skb->truesize;
> +                     rtnl_qdisc_drop(skb, q->l_queue);
> +                     /* Keep the overall qdisc stats consistent */
> +                     --sch->q.qlen;
> +                     qdisc_qstats_backlog_dec(sch, skb);

Sashiko says:
---
The drop counter is incremented for the L-queue via rtnl_qdisc_drop(),
but it appears the drop counter for the parent qdisc (sch) is not updated.
Will this cause user-facing statistics for the overall dualpi2 qdisc to
underreport drops?
---


Reply via email to