On Fri, 2016-04-29 at 13:35 -0400, Neil Horman wrote:
> This was recently reported to me, and reproduced on the latest net kernel, 
> when
> attempting to run netperf from a host that had a netem qdisc attached to the
> egress interface:
> 


>  /*
>   * Insert one skb into qdisc.
>   * Note: parent depends on return value to account for queue length.
> @@ -407,7 +426,11 @@ static int netem_enqueue(struct sk_buff *skb, struct 
> Qdisc *sch)
>       /* We don't fill cb now as skb_unshare() may invalidate it */
>       struct netem_skb_cb *cb;
>       struct sk_buff *skb2;
> +     struct sk_buff *segs = NULL;
> +     unsigned int len = 0, prev_len = qdisc_pkt_len(skb);
> +     int nb = 0;
>       int count = 1;
> +     int rc = NET_XMIT_SUCCESS;
>  
>       /* Random duplication */
>       if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
> @@ -453,10 +476,22 @@ static int netem_enqueue(struct sk_buff *skb, struct 
> Qdisc *sch)
>        * do it now in software before we mangle it.
>        */
>       if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
> +             if (skb_is_gso(skb)) {
> +                     segs = netem_segment(skb, sch);
> +                     if (!segs)
> +                             return NET_XMIT_DROP;
> +             } else
> +                     segs = skb;
> +
> +             skb = segs;
> +             segs = segs->next;
> +
>               if (!(skb = skb_unshare(skb, GFP_ATOMIC)) ||
>                   (skb->ip_summed == CHECKSUM_PARTIAL &&
> -                  skb_checksum_help(skb)))
> -                     return qdisc_drop(skb, sch);
> +                  skb_checksum_help(skb))) {
> +                     rc = qdisc_drop(skb, sch);
> +                     goto finish_segs;
> +             }
>  
>               skb->data[prandom_u32() % skb_headlen(skb)] ^=
>                       1<<(prandom_u32() % 8);
> @@ -516,7 +551,26 @@ static int netem_enqueue(struct sk_buff *skb, struct 
> Qdisc *sch)
>               sch->qstats.requeues++;
>       }
>  
> -     return NET_XMIT_SUCCESS;
> +finish_segs:
> +     if (segs) {
> +             while (segs) {
> +                     skb2 = segs->next;
> +                     segs->next = NULL;
> +                     qdisc_skb_cb(segs)->pkt_len = segs->len;
> +                     len += segs->len;

Wrong operation if packet is dropped by qdisc_enqueue() ?

I would use
 u32 last_len = segs->len;

> +                     rc = qdisc_enqueue(segs, sch);
> +                     if (rc != NET_XMIT_SUCCESS) {
> +                             if (net_xmit_drop_count(rc))
> +                                     qdisc_qstats_drop(sch);
> +                     } else

   } else {
        nb++;
        len += last_len;
   }

> +                             nb++;
> +                     segs = skb2;
> +             }
> +             sch->q.qlen += nb;
> +             if (nb > 1)
> +                     qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len);
> +     }
> +     return rc;

Seems you should return NET_XMIT_SUCCESS instead of status of last
segment ?

>  }

Thanks.


Reply via email to