]> err.no Git - linux-2.6/blobdiff - net/core/skbuff.c
[NET]: Avoid allocating skb in skb_pad
[linux-2.6] / net / core / skbuff.c
index bb7210f4005e1a781908238d0bfbbb30a42838a1..fe63d4efbd4dbae12672a8aa69f02e19b4d998ae 100644 (file)
@@ -781,24 +781,40 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
  *     filled. Used by network drivers which may DMA or transfer data
  *     beyond the buffer end onto the wire.
  *
- *     May return NULL in out of memory cases.
+ *     May return error in out of memory cases. The skb is freed on error.
  */
  
-struct sk_buff *skb_pad(struct sk_buff *skb, int pad)
+int skb_pad(struct sk_buff *skb, int pad)
 {
-       struct sk_buff *nskb;
+       int err;
+       int ntail;
        
        /* If the skbuff is non linear tailroom is always zero.. */
-       if (skb_tailroom(skb) >= pad) {
+       if (!skb_cloned(skb) && skb_tailroom(skb) >= pad) {
                memset(skb->data+skb->len, 0, pad);
-               return skb;
+               return 0;
        }
-       
-       nskb = skb_copy_expand(skb, skb_headroom(skb), skb_tailroom(skb) + pad, GFP_ATOMIC);
+
+       ntail = skb->data_len + pad - (skb->end - skb->tail);
+       if (likely(skb_cloned(skb) || ntail > 0)) {
+               err = pskb_expand_head(skb, 0, ntail, GFP_ATOMIC);
+               if (unlikely(err))
+                       goto free_skb;
+       }
+
+       /* FIXME: The use of this function with non-linear skb's really needs
+        * to be audited.
+        */
+       err = skb_linearize(skb);
+       if (unlikely(err))
+               goto free_skb;
+
+       memset(skb->data + skb->len, 0, pad);
+       return 0;
+
+free_skb:
        kfree_skb(skb);
-       if (nskb)
-               memset(nskb->data+nskb->len, 0, pad);
-       return nskb;
+       return err;
 }      
  
 /* Trims skb to length len. It can change skb pointers.