Commit 71d93b39e52e92aea35f1058d957cf12250d0b75

Authored by Herbert Xu
Committed by David S. Miller
1 parent 73cc19f155

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>

Showing 2 changed files with 61 additions and 0 deletions Side-by-side Diff

include/linux/skbuff.h
... ... @@ -1687,6 +1687,8 @@
1687 1687 int shiftlen);
1688 1688  
1689 1689 extern struct sk_buff *skb_segment(struct sk_buff *skb, int features);
  1690 +extern int skb_gro_receive(struct sk_buff **head,
  1691 + struct sk_buff *skb);
1690 1692  
1691 1693 static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
1692 1694 int len, void *buffer)
... ... @@ -2582,6 +2582,65 @@
2582 2582  
2583 2583 EXPORT_SYMBOL_GPL(skb_segment);
2584 2584  
  2585 +int 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 +
  2629 +merge:
  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 +}
  2642 +EXPORT_SYMBOL_GPL(skb_gro_receive);
  2643 +
2585 2644 void __init skb_init(void)
2586 2645 {
2587 2646 skbuff_head_cache = kmem_cache_create("skbuff_head_cache",