|
@@ -778,6 +778,28 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
|
|
|
|
|
|
size = SKB_DATA_ALIGN(size);
|
|
|
|
|
|
+ /* Check if we can avoid taking references on fragments if we own
|
|
|
+ * the last reference on skb->head. (see skb_release_data())
|
|
|
+ */
|
|
|
+ if (!skb->cloned)
|
|
|
+ fastpath = true;
|
|
|
+ else {
|
|
|
+ int delta = skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1;
|
|
|
+
|
|
|
+ fastpath = atomic_read(&skb_shinfo(skb)->dataref) == delta;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fastpath &&
|
|
|
+ size + sizeof(struct skb_shared_info) <= ksize(skb->head)) {
|
|
|
+ memmove(skb->head + size, skb_shinfo(skb),
|
|
|
+ offsetof(struct skb_shared_info,
|
|
|
+ frags[skb_shinfo(skb)->nr_frags]));
|
|
|
+ memmove(skb->head + nhead, skb->head,
|
|
|
+ skb_tail_pointer(skb) - skb->head);
|
|
|
+ off = nhead;
|
|
|
+ goto adjust_others;
|
|
|
+ }
|
|
|
+
|
|
|
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
|
|
|
if (!data)
|
|
|
goto nodata;
|
|
@@ -791,17 +813,6 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
|
|
|
skb_shinfo(skb),
|
|
|
offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags]));
|
|
|
|
|
|
- /* Check if we can avoid taking references on fragments if we own
|
|
|
- * the last reference on skb->head. (see skb_release_data())
|
|
|
- */
|
|
|
- if (!skb->cloned)
|
|
|
- fastpath = true;
|
|
|
- else {
|
|
|
- int delta = skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1;
|
|
|
-
|
|
|
- fastpath = atomic_read(&skb_shinfo(skb)->dataref) == delta;
|
|
|
- }
|
|
|
-
|
|
|
if (fastpath) {
|
|
|
kfree(skb->head);
|
|
|
} else {
|
|
@@ -816,6 +827,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
|
|
|
off = (data + nhead) - skb->head;
|
|
|
|
|
|
skb->head = data;
|
|
|
+adjust_others:
|
|
|
skb->data += off;
|
|
|
#ifdef NET_SKBUFF_DATA_USES_OFFSET
|
|
|
skb->end = size;
|