|
@@ -245,6 +245,7 @@ EXPORT_SYMBOL(__alloc_skb);
|
|
|
/**
|
|
|
* build_skb - build a network buffer
|
|
|
* @data: data buffer provided by caller
|
|
|
+ * @frag_size: size of fragment, or 0 if head was kmalloced
|
|
|
*
|
|
|
* Allocate a new &sk_buff. Caller provides space holding head and
|
|
|
* skb_shared_info. @data must have been allocated by kmalloc()
|
|
@@ -258,20 +259,21 @@ EXPORT_SYMBOL(__alloc_skb);
|
|
|
* before giving packet to stack.
|
|
|
* RX rings only contains data buffers, not full skbs.
|
|
|
*/
|
|
|
-struct sk_buff *build_skb(void *data)
|
|
|
+struct sk_buff *build_skb(void *data, unsigned int frag_size)
|
|
|
{
|
|
|
struct skb_shared_info *shinfo;
|
|
|
struct sk_buff *skb;
|
|
|
- unsigned int size;
|
|
|
+ unsigned int size = frag_size ? : ksize(data);
|
|
|
|
|
|
skb = kmem_cache_alloc(skbuff_head_cache, GFP_ATOMIC);
|
|
|
if (!skb)
|
|
|
return NULL;
|
|
|
|
|
|
- size = ksize(data) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
|
|
+ size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
|
|
|
|
|
memset(skb, 0, offsetof(struct sk_buff, tail));
|
|
|
skb->truesize = SKB_TRUESIZE(size);
|
|
|
+ skb->head_frag = frag_size != 0;
|
|
|
atomic_set(&skb->users, 1);
|
|
|
skb->head = data;
|
|
|
skb->data = data;
|
|
@@ -376,6 +378,14 @@ static void skb_clone_fraglist(struct sk_buff *skb)
|
|
|
skb_get(list);
|
|
|
}
|
|
|
|
|
|
+static void skb_free_head(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ if (skb->head_frag)
|
|
|
+ put_page(virt_to_head_page(skb->head));
|
|
|
+ else
|
|
|
+ kfree(skb->head);
|
|
|
+}
|
|
|
+
|
|
|
static void skb_release_data(struct sk_buff *skb)
|
|
|
{
|
|
|
if (!skb->cloned ||
|
|
@@ -402,7 +412,7 @@ static void skb_release_data(struct sk_buff *skb)
|
|
|
if (skb_has_frag_list(skb))
|
|
|
skb_drop_fraglist(skb);
|
|
|
|
|
|
- kfree(skb->head);
|
|
|
+ skb_free_head(skb);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -644,6 +654,7 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
|
|
|
C(tail);
|
|
|
C(end);
|
|
|
C(head);
|
|
|
+ C(head_frag);
|
|
|
C(data);
|
|
|
C(truesize);
|
|
|
atomic_set(&n->users, 1);
|
|
@@ -940,7 +951,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
|
|
|
fastpath = atomic_read(&skb_shinfo(skb)->dataref) == delta;
|
|
|
}
|
|
|
|
|
|
- if (fastpath &&
|
|
|
+ if (fastpath && !skb->head_frag &&
|
|
|
size + sizeof(struct skb_shared_info) <= ksize(skb->head)) {
|
|
|
memmove(skb->head + size, skb_shinfo(skb),
|
|
|
offsetof(struct skb_shared_info,
|
|
@@ -967,7 +978,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
|
|
|
offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags]));
|
|
|
|
|
|
if (fastpath) {
|
|
|
- kfree(skb->head);
|
|
|
+ skb_free_head(skb);
|
|
|
} else {
|
|
|
/* copy this zero copy skb frags */
|
|
|
if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
|
|
@@ -985,6 +996,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
|
|
|
off = (data + nhead) - skb->head;
|
|
|
|
|
|
skb->head = data;
|
|
|
+ skb->head_frag = 0;
|
|
|
adjust_others:
|
|
|
skb->data += off;
|
|
|
#ifdef NET_SKBUFF_DATA_USES_OFFSET
|