ppp: Reconstruct fragmented packets using frag lists instead of copying.
authorDavid S. Miller <davem@davemloft.net>
Fri, 21 Jan 2011 06:46:07 +0000 (22:46 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 21 Jan 2011 06:46:07 +0000 (22:46 -0800)
[paulus@samba.org: fixed a couple of bugs]

Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Paul Mackerras <paulus@samba.org>
drivers/net/ppp_generic.c

index 3d7a38eeacda1bc879c133cee3ca8b8362e0c5c4..1d4fb348488f12f309d45b682b3a2fcc431dfb68 100644 (file)
@@ -2055,16 +2055,6 @@ ppp_mp_reconstruct(struct ppp *ppp)
                                netdev_printk(KERN_DEBUG, ppp->dev,
                                              "PPP: reconstructed packet"
                                              " is too long (%d)\n", len);
-                       } else if (p == head) {
-                               /* fragment is complete packet - reuse skb */
-                               tail = p;
-                               skb = skb_get(p);
-                               break;
-                       } else if ((skb = dev_alloc_skb(len)) == NULL) {
-                               ++ppp->dev->stats.rx_missed_errors;
-                               netdev_printk(KERN_DEBUG, ppp->dev,
-                                             "PPP: no memory for "
-                                             "reconstructed packet");
                        } else {
                                tail = p;
                                break;
@@ -2097,16 +2087,33 @@ ppp_mp_reconstruct(struct ppp *ppp)
                        ppp_receive_error(ppp);
                }
 
-               if (head != tail)
-                       /* copy to a single skb */
-                       for (p = head; p != tail->next; p = p->next)
-                               skb_copy_bits(p, 0, skb_put(skb, p->len), p->len);
+               skb = head;
+               if (head != tail) {
+                       struct sk_buff **fragpp = &skb_shinfo(skb)->frag_list;
+                       p = skb_queue_next(list, head);
+                       __skb_unlink(skb, list);
+                       skb_queue_walk_from_safe(list, p, tmp) {
+                               __skb_unlink(p, list);
+                               *fragpp = p;
+                               p->next = NULL;
+                               fragpp = &p->next;
+
+                               skb->len += p->len;
+                               skb->data_len += p->len;
+                               skb->truesize += p->len;
+
+                               if (p == tail)
+                                       break;
+                       }
+               } else {
+                       __skb_unlink(skb, list);
+               }
+
                ppp->nextseq = PPP_MP_CB(tail)->sequence + 1;
                head = tail->next;
        }
 
-       /* Discard all the skbuffs that we have copied the data out of
-          or that we can't use. */
+       /* Discard all the skbuffs that we can't use. */
        while ((p = list->next) != head) {
                __skb_unlink(p, list);
                kfree_skb(p);