tunneling: Avoid extra copying if expanding headroom.
authorJesse Gross <jesse@nicira.com>
Wed, 8 Jun 2011 00:09:35 +0000 (17:09 -0700)
committerJesse Gross <jesse@nicira.com>
Thu, 16 Jun 2011 21:41:15 +0000 (14:41 -0700)
Currently if we need additional headroom before encapsulating a
packet a clone is made before expanding headroom or if we are
just trying to make the headroom writable then we copy both
the struct sk_buff and the paged data.  Both of these are unnecessary
and we end up freeing the original copy.  We can remove these copies
and simplify the code by just expanding the linear data area.

Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
datapath/tunnel.c

index 3263223..2bf6159 100644 (file)
@@ -1027,27 +1027,6 @@ static struct rtable *find_route(struct vport *vport,
        }
 }
 
-static struct sk_buff *check_headroom(struct sk_buff *skb, int headroom)
-{
-       if (skb_headroom(skb) < headroom || skb_header_cloned(skb)) {
-               struct sk_buff *nskb = skb_realloc_headroom(skb, headroom + 16);
-               if (unlikely(!nskb)) {
-                       kfree_skb(skb);
-                       return ERR_PTR(-ENOMEM);
-               }
-
-               set_skb_csum_bits(skb, nskb);
-
-               if (skb->sk)
-                       skb_set_owner_w(nskb, skb->sk);
-
-               kfree_skb(skb);
-               return nskb;
-       }
-
-       return skb;
-}
-
 static inline bool need_linearize(const struct sk_buff *skb)
 {
        int i;
@@ -1084,10 +1063,14 @@ static struct sk_buff *handle_offloads(struct sk_buff *skb,
                        + mutable->tunnel_hlen
                        + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
 
-       skb = check_headroom(skb, min_headroom);
-       if (IS_ERR(skb)) {
-               err = PTR_ERR(skb);
-               goto error;
+       if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) {
+               int head_delta = SKB_DATA_ALIGN(min_headroom -
+                                               skb_headroom(skb) +
+                                               16);
+               err = pskb_expand_head(skb, max_t(int, head_delta, 0),
+                                       0, GFP_ATOMIC);
+               if (unlikely(err))
+                       goto error_free;
        }
 
        if (skb_is_gso(skb)) {