summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/pppoe.c2
-rw-r--r--include/linux/skbuff.h40
-rw-r--r--net/bridge/br_netfilter.c2
3 files changed, 33 insertions, 11 deletions
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index bac36546e0bf..0d7f570b9a54 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -860,7 +860,7 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb)
/* Copy the data if there is no space for the header or if it's
* read-only.
*/
- if (skb_cow(skb, sizeof(*ph) + dev->hard_header_len))
+ if (skb_cow_head(skb, sizeof(*ph) + dev->hard_header_len))
goto abort;
__skb_push(skb, sizeof(*ph));
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 93c27f71122a..a656cecd373c 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1352,6 +1352,22 @@ static inline int skb_clone_writable(struct sk_buff *skb, int len)
skb_headroom(skb) + len <= skb->hdr_len;
}
+static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom,
+ int cloned)
+{
+ int delta = 0;
+
+ if (headroom < NET_SKB_PAD)
+ headroom = NET_SKB_PAD;
+ if (headroom > skb_headroom(skb))
+ delta = headroom - skb_headroom(skb);
+
+ if (delta || cloned)
+ return pskb_expand_head(skb, ALIGN(delta, NET_SKB_PAD), 0,
+ GFP_ATOMIC);
+ return 0;
+}
+
/**
* skb_cow - copy header of skb when it is required
* @skb: buffer to cow
@@ -1366,16 +1382,22 @@ static inline int skb_clone_writable(struct sk_buff *skb, int len)
*/
static inline int skb_cow(struct sk_buff *skb, unsigned int headroom)
{
- int delta = (headroom > NET_SKB_PAD ? headroom : NET_SKB_PAD) -
- skb_headroom(skb);
-
- if (delta < 0)
- delta = 0;
+ return __skb_cow(skb, headroom, skb_cloned(skb));
+}
- if (delta || skb_cloned(skb))
- return pskb_expand_head(skb, (delta + (NET_SKB_PAD-1)) &
- ~(NET_SKB_PAD-1), 0, GFP_ATOMIC);
- return 0;
+/**
+ * skb_cow_head - skb_cow but only making the head writable
+ * @skb: buffer to cow
+ * @headroom: needed headroom
+ *
+ * This function is identical to skb_cow except that we replace the
+ * skb_cloned check by skb_header_cloned. It should be used when
+ * you only need to push on some header and do not need to modify
+ * the data.
+ */
+static inline int skb_cow_head(struct sk_buff *skb, unsigned int headroom)
+{
+ return __skb_cow(skb, headroom, skb_header_cloned(skb));
}
/**
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 3ee2022928e3..fc13130035e7 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -183,7 +183,7 @@ int nf_bridge_copy_header(struct sk_buff *skb)
int err;
int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
- err = skb_cow(skb, header_size);
+ err = skb_cow_head(skb, header_size);
if (err)
return err;