aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2008-12-16 02:42:33 -0500
committerDavid S. Miller <davem@davemloft.net>2008-12-16 02:42:33 -0500
commit71d93b39e52e92aea35f1058d957cf12250d0b75 (patch)
tree270e3fe674d15467454504a99af2146c0324e08b
parent73cc19f1556b95976934de236fd9043f7208844f (diff)
net: Add skb_gro_receive
This patch adds the helper skb_gro_receive to merge packets for GRO. The current method is to allocate a new header skb and then chain the original packets to its frag_list. This is done to make it easier to integrate into the existing GSO framework. In future as GSO is moved into the drivers, we can undo this and simply chain the original packets together. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/skbuff.h2
-rw-r--r--net/core/skbuff.c59
2 files changed, 61 insertions, 0 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index acf17af45af9..cf2cb50f77d1 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1687,6 +1687,8 @@ extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb,
1687 int shiftlen); 1687 int shiftlen);
1688 1688
1689extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); 1689extern struct sk_buff *skb_segment(struct sk_buff *skb, int features);
1690extern int skb_gro_receive(struct sk_buff **head,
1691 struct sk_buff *skb);
1690 1692
1691static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, 1693static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
1692 int len, void *buffer) 1694 int len, void *buffer)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 18e224af05a6..b8d0abb26433 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2582,6 +2582,65 @@ err:
2582 2582
2583EXPORT_SYMBOL_GPL(skb_segment); 2583EXPORT_SYMBOL_GPL(skb_segment);
2584 2584
2585int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
2586{
2587 struct sk_buff *p = *head;
2588 struct sk_buff *nskb;
2589 unsigned int headroom;
2590 unsigned int hlen = p->data - skb_mac_header(p);
2591
2592 if (hlen + p->len + skb->len >= 65536)
2593 return -E2BIG;
2594
2595 if (skb_shinfo(p)->frag_list)
2596 goto merge;
2597
2598 headroom = skb_headroom(p);
2599 nskb = netdev_alloc_skb(p->dev, headroom);
2600 if (unlikely(!nskb))
2601 return -ENOMEM;
2602
2603 __copy_skb_header(nskb, p);
2604 nskb->mac_len = p->mac_len;
2605
2606 skb_reserve(nskb, headroom);
2607
2608 skb_set_mac_header(nskb, -hlen);
2609 skb_set_network_header(nskb, skb_network_offset(p));
2610 skb_set_transport_header(nskb, skb_transport_offset(p));
2611
2612 memcpy(skb_mac_header(nskb), skb_mac_header(p), hlen);
2613
2614 *NAPI_GRO_CB(nskb) = *NAPI_GRO_CB(p);
2615 skb_shinfo(nskb)->frag_list = p;
2616 skb_header_release(p);
2617 nskb->prev = p;
2618
2619 nskb->data_len += p->len;
2620 nskb->truesize += p->len;
2621 nskb->len += p->len;
2622
2623 *head = nskb;
2624 nskb->next = p->next;
2625 p->next = NULL;
2626
2627 p = nskb;
2628
2629merge:
2630 NAPI_GRO_CB(p)->count++;
2631 p->prev->next = skb;
2632 p->prev = skb;
2633 skb_header_release(skb);
2634
2635 p->data_len += skb->len;
2636 p->truesize += skb->len;
2637 p->len += skb->len;
2638
2639 NAPI_GRO_CB(skb)->same_flow = 1;
2640 return 0;
2641}
2642EXPORT_SYMBOL_GPL(skb_gro_receive);
2643
2585void __init skb_init(void) 2644void __init skb_init(void)
2586{ 2645{
2587 skbuff_head_cache = kmem_cache_create("skbuff_head_cache", 2646 skbuff_head_cache = kmem_cache_create("skbuff_head_cache",