]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
[NET]: Avoid allocating skb in skb_pad
authorHerbert Xu <herbert@gondor.apana.org.au>
Fri, 23 Jun 2006 09:06:41 +0000 (02:06 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 23 Jun 2006 09:06:41 +0000 (02:06 -0700)
First of all it is unnecessary to allocate a new skb in skb_pad since
the existing one is not shared.  More importantly, our hard_start_xmit
interface does not allow a new skb to be allocated since that breaks
requeueing.

This patch uses pskb_expand_head to expand the existing skb and linearize
it if needed.  Actually, someone should sift through every instance of
skb_pad on a non-linear skb as they do not fit the reasons why this was
originally created.

Incidentally, this fixes a minor bug when the skb is cloned (tcpdump,
TCP, etc.).  As it is skb_pad will simply write over a cloned skb.  Because
of the position of the write it is unlikely to cause problems but still
it's best if we don't do it.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
36 files changed:
drivers/net/3c527.c
drivers/net/82596.c
drivers/net/a2065.c
drivers/net/ariadne.c
drivers/net/arm/ether1.c
drivers/net/arm/ether3.c
drivers/net/atarilance.c
drivers/net/cassini.c
drivers/net/declance.c
drivers/net/depca.c
drivers/net/eepro.c
drivers/net/eexpress.c
drivers/net/epic100.c
drivers/net/eth16i.c
drivers/net/hp100.c
drivers/net/lance.c
drivers/net/lasi_82596.c
drivers/net/lp486e.c
drivers/net/myri10ge/myri10ge.c
drivers/net/pcmcia/fmvj18x_cs.c
drivers/net/pcmcia/xirc2ps_cs.c
drivers/net/r8169.c
drivers/net/seeq8005.c
drivers/net/sis190.c
drivers/net/sk98lin/skge.c
drivers/net/skge.c
drivers/net/smc9194.c
drivers/net/sonic.c
drivers/net/starfire.c
drivers/net/via-rhine.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/wavelan_cs.c
drivers/net/yellowfin.c
drivers/net/znet.c
include/linux/skbuff.h
net/core/skbuff.c

index 1b1cb0026072e71d200d6a7e8fd01dadf9582661..157eda57392536df2af842f1baf36c82bb0792e7 100644 (file)
@@ -1031,8 +1031,7 @@ static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
                return 1;
        }
 
-       skb = skb_padto(skb, ETH_ZLEN);
-       if (skb == NULL) {
+       if (skb_padto(skb, ETH_ZLEN)) {
                netif_wake_queue(dev);
                return 0;
        }
index da0c878dcba8a4ee7b63255162834d340115f1cd..8a9f7d61b9b1e51b161a61d3e176b108df403a10 100644 (file)
@@ -1070,8 +1070,7 @@ static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                skb->len, (unsigned int)skb->data));
 
        if (skb->len < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 79bb56b8dcef00fa283284497004c0f7ba3a5dcd..71165ac0257a4deba97110e011f7fee23e0e247b 100644 (file)
@@ -573,8 +573,7 @@ static int lance_start_xmit (struct sk_buff *skb, struct net_device *dev)
        
        if (len < ETH_ZLEN) {
                len = ETH_ZLEN;
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
        }
 
index d1b6b1f794e214ced2db99c3d14c8a68fd9ae480..a9bb7a4aff9842dcd1413940882ec2e551085436 100644 (file)
@@ -607,8 +607,7 @@ static int ariadne_start_xmit(struct sk_buff *skb, struct net_device *dev)
     /* FIXME: is the 79C960 new enough to do its own padding right ? */
     if (skb->len < ETH_ZLEN)
     {
-       skb = skb_padto(skb, ETH_ZLEN);
-       if (skb == NULL)
+       if (skb_padto(skb, ETH_ZLEN))
            return 0;
        len = ETH_ZLEN;
     }
index 36475eb2727f6ae10e5b1617951b83e1910ea25a..312955d07b282f0b95e33ed631b365d4b40ddf69 100644 (file)
@@ -700,8 +700,7 @@ ether1_sendpacket (struct sk_buff *skb, struct net_device *dev)
        }
 
        if (skb->len < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        goto out;
        }
 
index f1d5b1027ff7ef81e432eeea641ec4bb0c750174..081074180e62dc2f029146de075dc316dd4f642b 100644 (file)
@@ -518,8 +518,7 @@ ether3_sendpacket(struct sk_buff *skb, struct net_device *dev)
 
        length = (length + 1) & ~1;
        if (length != skb->len) {
-               skb = skb_padto(skb, length);
-               if (skb == NULL)
+               if (skb_padto(skb, length))
                        goto out;
        }
 
index 442b2cbeb58a739507efca61d12f69139486d064..91783a8008be13be058d4081ad196b613336937f 100644 (file)
@@ -804,8 +804,7 @@ static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
                ++len;
                
        if (len > skb->len) {
-               skb = skb_padto(skb, len);
-               if (skb == NULL)
+               if (skb_padto(skb, len))
                        return 0;
        }
                
index 39f36aa05aa8b005bf4b2b2638ef7f1a47810f6e..565a54f1d06a1852bac76a8c317906bd04dbcac9 100644 (file)
@@ -2915,8 +2915,7 @@ static int cas_start_xmit(struct sk_buff *skb, struct net_device *dev)
         */
        static int ring; 
 
-       skb = skb_padto(skb, cp->min_frame_size);
-       if (!skb)
+       if (skb_padto(skb, cp->min_frame_size))
                return 0;
 
        /* XXX: we need some higher-level QoS hooks to steer packets to
index f130bdab3fd319f06a1fac0101737d5149b381f2..d3d958e7ac56bc55dce349c303244f48d006515f 100644 (file)
@@ -885,8 +885,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
        len = skblen;
        
        if (len < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                len = ETH_ZLEN;
        }
index 0941d40f046f326b84d68a84ca351398739d503a..e946c43d3b10ad23ec9a0deae5c536a53cc2cbaf 100644 (file)
@@ -938,11 +938,8 @@ static int depca_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (skb->len < 1)
                goto out;
 
-       if (skb->len < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
-                       goto out;
-       }
+       if (skb_padto(skb, ETH_ZLEN))
+               goto out;
        
        netif_stop_queue(dev);
 
index a806dfe54d23c1a7ff58f0900e0c7b98076d09bc..e70f172699dbfe4f0cb6196d187f9dbb840e02c7 100644 (file)
@@ -1154,8 +1154,7 @@ static int eepro_send_packet(struct sk_buff *skb, struct net_device *dev)
                printk(KERN_DEBUG  "%s: entering eepro_send_packet routine.\n", dev->name);
 
        if (length < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 82bd356e4f3abf8d2ecf7ff909f8ce4b7bda149f..a74b20715755d4ff5dd63f98aa57e53248c41b1e 100644 (file)
@@ -677,8 +677,7 @@ static int eexp_xmit(struct sk_buff *buf, struct net_device *dev)
 #endif
 
        if (buf->len < ETH_ZLEN) {
-               buf = skb_padto(buf, ETH_ZLEN);
-               if (buf == NULL)
+               if (skb_padto(buf, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 8d680ce600d7cb407948d32105d7c3b33d797839..724d7dc35fa3afbca55b44cd170aa2b51ecb30de 100644 (file)
@@ -1027,11 +1027,8 @@ static int epic_start_xmit(struct sk_buff *skb, struct net_device *dev)
        u32 ctrl_word;
        unsigned long flags;
 
-       if (skb->len < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
-                       return 0;
-       }
+       if (skb_padto(skb, ETH_ZLEN))
+               return 0;
 
        /* Caution: the write order is important here, set the field with the
           "ownership" bit last. */
index b67545be2caa346fa68e7f90f270a81ce490cf0a..4bf76f86d8e90081f8f796369daeaff0827cfc08 100644 (file)
@@ -1064,8 +1064,7 @@ static int eth16i_tx(struct sk_buff *skb, struct net_device *dev)
        unsigned long flags;
 
        if (length < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 247c8ca86033693bfe99b01680ede5a54a3fe5f2..dd1dc32dc98dc3181db5bb41be53f550bd3e1844 100644 (file)
@@ -1487,11 +1487,8 @@ static int hp100_start_xmit_bm(struct sk_buff *skb, struct net_device *dev)
        if (skb->len <= 0)
                return 0;
                
-       if (skb->len < ETH_ZLEN && lp->chip == HP100_CHIPID_SHASTA) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
-                       return 0;
-       }
+       if (lp->chip == HP100_CHIPID_SHASTA && skb_padto(skb, ETH_ZLEN))
+               return 0;
 
        /* Get Tx ring tail pointer */
        if (lp->txrtail->next == lp->txrhead) {
index bb5ad479210b17f8b93498912926e461da854090..c1c3452c90ca2696ac82edd8ccf865be33a1264b 100644 (file)
@@ -968,8 +968,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
        /* The old LANCE chips doesn't automatically pad buffers to min. size. */
        if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) {
                if (skb->len < ETH_ZLEN) {
-                       skb = skb_padto(skb, ETH_ZLEN);
-                       if (skb == NULL)
+                       if (skb_padto(skb, ETH_ZLEN))
                                goto out;
                        lp->tx_ring[entry].length = -ETH_ZLEN;
                }
index 957888de3d7e8578c0f05ad4616a4d688967a26e..1ab09447baa551d6eece478f079f3ee0aef674dc 100644 (file)
@@ -1083,8 +1083,7 @@ static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                skb->len, skb->data));
 
        if (length < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 94d5ea1ce8bd9b9c9e14f05694e699c387eff662..bf3f343ae7153dc56bc9107da56f4cdf4f3bcdf1 100644 (file)
@@ -877,8 +877,7 @@ static int i596_start_xmit (struct sk_buff *skb, struct net_device *dev) {
        length = skb->len;
        
        if (length < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 5a74f63618bccd8e533b76b7cba3adffb0edd379..b983e1e04348f4a604917ce73e46f52e20020a42 100644 (file)
@@ -1939,8 +1939,7 @@ again:
 
                /* pad frames to at least ETH_ZLEN bytes */
                if (unlikely(skb->len < ETH_ZLEN)) {
-                       skb = skb_padto(skb, ETH_ZLEN);
-                       if (skb == NULL) {
+                       if (skb_padto(skb, ETH_ZLEN)) {
                                /* The packet is gone, so we must
                                 * return 0 */
                                mgp->stats.tx_dropped += 1;
index 09b11761cdfabf6a4acf700cc2b4dfca0791391c..ea93b8f186053f94ab88abff7b15fc9ce9db1f2a 100644 (file)
@@ -831,8 +831,7 @@ static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev)
     
     if (length < ETH_ZLEN)
     {
-       skb = skb_padto(skb, ETH_ZLEN);
-       if (skb == NULL)
+       if (skb_padto(skb, ETH_ZLEN))
                return 0;
        length = ETH_ZLEN;
     }
index e80d1e3aec68e4f9eee3c03994ad4e64184730a2..9bae77ce13141d883a9f08ce9cc9d0ae38b39614 100644 (file)
@@ -1374,8 +1374,7 @@ do_start_xmit(struct sk_buff *skb, struct net_device *dev)
      */
     if (pktlen < ETH_ZLEN)
     {
-        skb = skb_padto(skb, ETH_ZLEN);
-        if (skb == NULL)
+        if (skb_padto(skb, ETH_ZLEN))
                return 0;
        pktlen = ETH_ZLEN;
     }
index 9945cc6b8d90ac1fbdfca8ebad2cc6a5320c6cef..985afe0e62737119716af17d46c700fe79a2b179 100644 (file)
@@ -2222,8 +2222,7 @@ static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev)
                len = skb->len;
 
                if (unlikely(len < ETH_ZLEN)) {
-                       skb = skb_padto(skb, ETH_ZLEN);
-                       if (!skb)
+                       if (skb_padto(skb, ETH_ZLEN))
                                goto err_update_stats;
                        len = ETH_ZLEN;
                }
index bcef03feb2fc571ffe5a8eaeb5f12034e03b9d3a..efd0f235020fb4e7099174d70232aece63581c0d 100644 (file)
@@ -396,8 +396,7 @@ static int seeq8005_send_packet(struct sk_buff *skb, struct net_device *dev)
        unsigned char *buf;
 
        if (length < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 31dd3f036fa84cc0f3298efd5f8c9e0fb96897df..df39f34476550613891392a24e4df5e854a626ca 100644 (file)
@@ -1156,8 +1156,7 @@ static int sis190_start_xmit(struct sk_buff *skb, struct net_device *dev)
        dma_addr_t mapping;
 
        if (unlikely(skb->len < ETH_ZLEN)) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (!skb) {
+               if (skb_padto(skb, ETH_ZLEN)) {
                        tp->stats.tx_dropped++;
                        goto out;
                }
index 38a26df4095f33976dc05bca3b405fe3cf9c64dc..f3efbd177ae7deda0b3a72979655f87a0a1d418d 100644 (file)
@@ -1525,7 +1525,7 @@ struct sk_buff    *pMessage)      /* pointer to send-message              */
        ** This is to resolve faulty padding by the HW with 0xaa bytes.
        */
        if (BytesSend < C_LEN_ETHERNET_MINSIZE) {
-               if ((pMessage = skb_padto(pMessage, C_LEN_ETHERNET_MINSIZE)) == NULL) {
+               if (skb_padto(pMessage, C_LEN_ETHERNET_MINSIZE)) {
                        spin_unlock_irqrestore(&pTxPort->TxDesRingLock, Flags);
                        return 0;
                }
index 536dd1cf7f79281e9b9d2516d4ff58ff7eeea933..19a4a16055dc2435e13a0103d90663dde5485ac4 100644 (file)
@@ -2310,8 +2310,7 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev)
        u64 map;
        unsigned long flags;
 
-       skb = skb_padto(skb, ETH_ZLEN);
-       if (!skb)
+       if (skb_padto(skb, ETH_ZLEN))
                return NETDEV_TX_OK;
 
        if (!spin_trylock_irqsave(&skge->tx_lock, flags))
index 6cf16f322ad50df76c15c38eae08c9b1e370dd4f..8b0321f1976c7cdb128a159081ed86623ac8d1f7 100644 (file)
@@ -523,8 +523,7 @@ static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * de
        length = skb->len;
 
        if (length < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL) {
+               if (skb_padto(skb, ETH_ZLEN)) {
                        netif_wake_queue(dev);
                        return 0;
                }
index 90b818a8de6e41ca6d7f0863525e87c057ce6cfb..cab0dd958492cd6efeec5c8d25342f99e8e25359 100644 (file)
@@ -231,8 +231,7 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev)
 
        length = skb->len;
        if (length < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 9b7805be21dab116f0dd205efbc7022f6a5feb25..c158eedc78135d60fd1b01456f10aaa394238bf2 100644 (file)
@@ -1349,8 +1349,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
 
 #if defined(ZEROCOPY) && defined(HAS_BROKEN_FIRMWARE)
        if (skb->ip_summed == CHECKSUM_HW) {
-               skb = skb_padto(skb, (skb->len + PADDING_MASK) & ~PADDING_MASK);
-               if (skb == NULL)
+               if (skb_padto(skb, (skb->len + PADDING_MASK) & ~PADDING_MASK))
                        return NETDEV_TX_OK;
        }
 #endif /* ZEROCOPY && HAS_BROKEN_FIRMWARE */
index fdc21037f6dcf8204bf8950e7d294527312c1091..c80a4f1d5f7a958dc42e29856780925e2249a36e 100644 (file)
@@ -1284,11 +1284,8 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev)
        /* Calculate the next Tx descriptor entry. */
        entry = rp->cur_tx % TX_RING_SIZE;
 
-       if (skb->len < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
-                       return 0;
-       }
+       if (skb_padto(skb, ETH_ZLEN))
+               return 0;
 
        rp->tx_skbuff[entry] = skb;
 
index 879eb427607ca772f9406b8c83bb4e253434f850..a915fe6c6aa5d441fea41835f4ac6a04ae583b2a 100644 (file)
@@ -924,8 +924,7 @@ static int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
     if (length < ETH_ZLEN)
     {
-       skb = skb_padto(skb, ETH_ZLEN);
-       if (skb == NULL)
+       if (skb_padto(skb, ETH_ZLEN))
                return 0;
        length = ETH_ZLEN;
     }
index f7724eb2fa7ee35373e41d5f6128612c0dc8a073..561250f73fd38e6d0acbcb92d27184838305b30f 100644 (file)
@@ -3194,11 +3194,8 @@ wavelan_packet_xmit(struct sk_buff *     skb,
         * and we don't have the Ethernet specific requirement of beeing
         * able to detect collisions, therefore in theory we don't really
         * need to pad. Jean II */
-       if (skb->len < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
-                       return 0;
-       }
+       if (skb_padto(skb, ETH_ZLEN))
+               return 0;
 
   wv_packet_write(dev, skb->data, skb->len);
 
index fd0f43b7db5b089248d27f90fe0851ee2b7b382c..ecec8e5db786e66dc4128edd216d8cb8a0072eb8 100644 (file)
@@ -862,13 +862,11 @@ static int yellowfin_start_xmit(struct sk_buff *skb, struct net_device *dev)
                /* Fix GX chipset errata. */
                if (cacheline_end > 24  || cacheline_end == 0) {
                        len = skb->len + 32 - cacheline_end + 1;
-                       if (len != skb->len)
-                               skb = skb_padto(skb, len);
-               }
-               if (skb == NULL) {
-                       yp->tx_skbuff[entry] = NULL;
-                       netif_wake_queue(dev);
-                       return 0;
+                       if (skb_padto(skb, len)) {
+                               yp->tx_skbuff[entry] = NULL;
+                               netif_wake_queue(dev);
+                               return 0;
+                       }
                }
        }
        yp->tx_skbuff[entry] = skb;
index 3ac047bc727d0f3c4cce4c68bf3336c19586289e..a7c089df66e6e0a0619e4f28b4a3fd05f9f9c6d8 100644 (file)
@@ -544,8 +544,7 @@ static int znet_send_packet(struct sk_buff *skb, struct net_device *dev)
                printk(KERN_DEBUG "%s: ZNet_send_packet.\n", dev->name);
 
        if (length < ETH_ZLEN) {
-               skb = skb_padto(skb, ETH_ZLEN);
-               if (skb == NULL)
+               if (skb_padto(skb, ETH_ZLEN))
                        return 0;
                length = ETH_ZLEN;
        }
index 66f8819f9568d0f248b363d65478c8b660962c35..f8c7eb79a27fbf3f1af6467d5eceb88b5253c4e3 100644 (file)
@@ -345,7 +345,7 @@ extern struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
 extern struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
                                       int newheadroom, int newtailroom,
                                       gfp_t priority);
-extern struct sk_buff *                skb_pad(struct sk_buff *skb, int pad);
+extern int            skb_pad(struct sk_buff *skb, int pad);
 #define dev_kfree_skb(a)       kfree_skb(a)
 extern void          skb_over_panic(struct sk_buff *skb, int len,
                                     void *here);
@@ -1122,16 +1122,15 @@ static inline int skb_cow(struct sk_buff *skb, unsigned int headroom)
  *
  *     Pads up a buffer to ensure the trailing bytes exist and are
  *     blanked. If the buffer already contains sufficient data it
- *     is untouched. Returns the buffer, which may be a replacement
- *     for the original, or NULL for out of memory - in which case
- *     the original buffer is still freed.
+ *     is untouched. Otherwise it is extended. Returns zero on
+ *     success. The skb is freed on error.
  */
  
-static inline struct sk_buff *skb_padto(struct sk_buff *skb, unsigned int len)
+static inline int skb_padto(struct sk_buff *skb, unsigned int len)
 {
        unsigned int size = skb->len;
        if (likely(size >= len))
-               return skb;
+               return 0;
        return skb_pad(skb, len-size);
 }
 
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.