diff options
-rw-r--r-- | include/linux/skbuff.h | 2 | ||||
-rw-r--r-- | net/core/skbuff.c | 59 |
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 | ||
1689 | extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); | 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 | static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, | 1693 | static 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 | ||
2583 | EXPORT_SYMBOL_GPL(skb_segment); | 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 | void __init skb_init(void) | 2644 | void __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", |