diff options
author | David S. Miller <davem@davemloft.net> | 2011-01-20 22:46:07 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-01-20 22:46:07 -0800 |
commit | 212bfb9e94e86b40684076f642b089b0565455d2 (patch) | |
tree | 3fef8a449d5c6b665beebaacb6d4398143b4106c /drivers/net/ppp_generic.c | |
parent | b48f8c23c336d82c1af9a53187568bdb6e86b8a8 (diff) | |
download | lwn-212bfb9e94e86b40684076f642b089b0565455d2.tar.gz lwn-212bfb9e94e86b40684076f642b089b0565455d2.zip |
ppp: Reconstruct fragmented packets using frag lists instead of copying.
[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>
Diffstat (limited to 'drivers/net/ppp_generic.c')
-rw-r--r-- | drivers/net/ppp_generic.c | 39 |
1 files changed, 23 insertions, 16 deletions
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 3d7a38eeacda..1d4fb348488f 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -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); |