Commit 71d93b39e52e92aea35f1058d957cf12250d0b75
Committed by
David S. Miller
1 parent
73cc19f155
Exists in
master
and in
40 other branches
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) |
net/core/skbuff.c
... | ... | @@ -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", |