@@ -613,6 +613,7 @@ struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src)
}
EXPORT_SYMBOL_GPL(skb_morph);
+static int __pskb_copy(struct sk_buff *skb, struct sk_buff *n);
/**
* skb_clone - duplicate an sk_buff
* @skb: buffer to clone
@@ -631,6 +632,20 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
{
struct sk_buff *n;
+ if (skb->emerg_dev) {
+ n = skb_dequeue(&skb->emerg_dev->rx_recycle);
+ if (!n)
+ goto norm_clone;
+ /* remove earlier reservers */
+ skb_reserve(n, - skb_headroom(n));
+ if (!__pskb_copy(skb, n)) {
+ n->emerg_dev = skb->emerg_dev;
+ dev_hold(skb->emerg_dev);
+ return n;
+ }
+ net_recycle_add(skb->emerg_dev, n);
+ }
+norm_clone:
n = skb + 1;
if (skb->fclone == SKB_FCLONE_ORIG &&
n->fclone == SKB_FCLONE_UNAVAILABLE) {
@@ -720,31 +735,22 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
EXPORT_SYMBOL(skb_copy);
/**
- * pskb_copy - create copy of an sk_buff with private head.
- * @skb: buffer to copy
- * @gfp_mask: allocation priority
+ * __pskb_copy - create copy of an sk_buff with private head.
+ * @skb: buffer to copy
+ * @n: skb to copy it
*
- * Make a copy of both an &sk_buff and part of its data, located
- * in header. Fragmented data remain shared. This is used when
- * the caller wishes to modify only header of &sk_buff and needs
- * private copy of the header to alter. Returns %NULL on failure
- * or the pointer to the buffer on success.
- * The returned buffer has a reference count of 1.
+ * This functions behaves like pskb_copy() except that it takes
+ * an allready allocated skb where it will copy head and data.
+ * The returned buffer has a reference count of 1.
*/
-
-struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)
+static int __pskb_copy(struct sk_buff *skb, struct sk_buff *n)
{
- /*
- * Allocate the copy buffer
- */
- struct sk_buff *n;
#ifdef NET_SKBUFF_DATA_USES_OFFSET
- n = alloc_skb(skb->end, gfp_mask);
+ if (skb->end > n->end)
#else
- n = alloc_skb(skb->end - skb->head, gfp_mask);
+ if ((skb->end - skb->head) > (n->end - n->head))
#endif
- if (!n)
- goto out;
+ return -EMSGSIZE;
/* Set the data pointer */
skb_reserve(n, skb->data - skb->head);
@@ -773,8 +779,40 @@ struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)
}
copy_skb_header(n, skb);
-out:
- return n;
+ return 0;
+}
+
+/**
+ * pskb_copy - create copy of an sk_buff with private head.
+ * @skb: buffer to copy
+ * @gfp_mask: allocation priority
+ *
+ * Make a copy of both an &sk_buff and part of its data, located
+ * in header. Fragmented data remain shared. This is used when
+ * the caller wishes to modify only header of &sk_buff and needs
+ * private copy of the header to alter. Returns %NULL on failure
+ * or the pointer to the buffer on success.
+ * The returned buffer has a reference count of 1.
+ */
+
+struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)
+{
+ /*
+ * Allocate the copy buffer
+ */
+ struct sk_buff *n;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+ n = alloc_skb(skb->end, gfp_mask);
+#else
+ n = alloc_skb(skb->end - skb->head, gfp_mask);
+#endif
+ if (!n)
+ return NULL;
+ if (!__pskb_copy(skb, n))
+ return n;
+ kfree_skb(n);
+ return NULL;
+
}
EXPORT_SYMBOL(pskb_copy);