aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netdevice.h2
-rw-r--r--include/linux/skbuff.h1
-rw-r--r--net/core/dev.c5
-rw-r--r--net/core/skbuff.c27
4 files changed, 33 insertions, 2 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index e0b70e961e61..7f377fb8b527 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1509,6 +1509,8 @@ struct napi_gro_cb {
1509 1509
1510 /* Free the skb? */ 1510 /* Free the skb? */
1511 int free; 1511 int free;
1512#define NAPI_GRO_FREE 1
1513#define NAPI_GRO_FREE_STOLEN_HEAD 2
1512}; 1514};
1513 1515
1514#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb) 1516#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 9d28a22a8554..2c75e98953b2 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -561,6 +561,7 @@ static inline struct rtable *skb_rtable(const struct sk_buff *skb)
561extern void kfree_skb(struct sk_buff *skb); 561extern void kfree_skb(struct sk_buff *skb);
562extern void consume_skb(struct sk_buff *skb); 562extern void consume_skb(struct sk_buff *skb);
563extern void __kfree_skb(struct sk_buff *skb); 563extern void __kfree_skb(struct sk_buff *skb);
564extern struct kmem_cache *skbuff_head_cache;
564extern struct sk_buff *__alloc_skb(unsigned int size, 565extern struct sk_buff *__alloc_skb(unsigned int size,
565 gfp_t priority, int fclone, int node); 566 gfp_t priority, int fclone, int node);
566extern struct sk_buff *build_skb(void *data, unsigned int frag_size); 567extern struct sk_buff *build_skb(void *data, unsigned int frag_size);
diff --git a/net/core/dev.c b/net/core/dev.c
index 501f3cc703dd..a2be59fe6ab8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3546,7 +3546,10 @@ gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
3546 break; 3546 break;
3547 3547
3548 case GRO_MERGED_FREE: 3548 case GRO_MERGED_FREE:
3549 consume_skb(skb); 3549 if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD)
3550 kmem_cache_free(skbuff_head_cache, skb);
3551 else
3552 __kfree_skb(skb);
3550 break; 3553 break;
3551 3554
3552 case GRO_HELD: 3555 case GRO_HELD:
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index effa75d0e318..09cc38651b2f 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -69,7 +69,7 @@
69#include <trace/events/skb.h> 69#include <trace/events/skb.h>
70#include <linux/highmem.h> 70#include <linux/highmem.h>
71 71
72static struct kmem_cache *skbuff_head_cache __read_mostly; 72struct kmem_cache *skbuff_head_cache __read_mostly;
73static struct kmem_cache *skbuff_fclone_cache __read_mostly; 73static struct kmem_cache *skbuff_fclone_cache __read_mostly;
74 74
75static void sock_pipe_buf_release(struct pipe_inode_info *pipe, 75static void sock_pipe_buf_release(struct pipe_inode_info *pipe,
@@ -2901,6 +2901,31 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
2901 2901
2902 NAPI_GRO_CB(skb)->free = 1; 2902 NAPI_GRO_CB(skb)->free = 1;
2903 goto done; 2903 goto done;
2904 } else if (skb->head_frag) {
2905 int nr_frags = pinfo->nr_frags;
2906 skb_frag_t *frag = pinfo->frags + nr_frags;
2907 struct page *page = virt_to_head_page(skb->head);
2908 unsigned int first_size = headlen - offset;
2909 unsigned int first_offset;
2910
2911 if (nr_frags + 1 + skbinfo->nr_frags > MAX_SKB_FRAGS)
2912 return -E2BIG;
2913
2914 first_offset = skb->data -
2915 (unsigned char *)page_address(page) +
2916 offset;
2917
2918 pinfo->nr_frags = nr_frags + 1 + skbinfo->nr_frags;
2919
2920 frag->page.p = page;
2921 frag->page_offset = first_offset;
2922 skb_frag_size_set(frag, first_size);
2923
2924 memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags);
2925 /* We dont need to clear skbinfo->nr_frags here */
2926
2927 NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE_STOLEN_HEAD;
2928 goto done;
2904 } else if (skb_gro_len(p) != pinfo->gso_size) 2929 } else if (skb_gro_len(p) != pinfo->gso_size)
2905 return -E2BIG; 2930 return -E2BIG;
2906 2931