packet: tx timestamping on tpacket ring
[cascardo/linux.git] / net / core / skbuff.c
index 33245ef..af9185d 100644 (file)
@@ -179,6 +179,33 @@ out:
  *
  */
 
+struct sk_buff *__alloc_skb_head(gfp_t gfp_mask, int node)
+{
+       struct sk_buff *skb;
+
+       /* Get the HEAD */
+       skb = kmem_cache_alloc_node(skbuff_head_cache,
+                                   gfp_mask & ~__GFP_DMA, node);
+       if (!skb)
+               goto out;
+
+       /*
+        * Only clear those fields we need to clear, not those that we will
+        * actually initialise below. Hence, don't put any more fields after
+        * the tail pointer in struct sk_buff!
+        */
+       memset(skb, 0, offsetof(struct sk_buff, tail));
+       skb->data = NULL;
+       skb->truesize = sizeof(struct sk_buff);
+       atomic_set(&skb->users, 1);
+
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       skb->mac_header = ~0U;
+#endif
+out:
+       return skb;
+}
+
 /**
  *     __alloc_skb     -       allocate a network buffer
  *     @size: size to allocate
@@ -584,7 +611,8 @@ static void skb_release_head_state(struct sk_buff *skb)
 static void skb_release_all(struct sk_buff *skb)
 {
        skb_release_head_state(skb);
-       skb_release_data(skb);
+       if (likely(skb->data))
+               skb_release_data(skb);
 }
 
 /**
@@ -673,6 +701,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->mac_header         = old->mac_header;
        new->inner_transport_header = old->inner_transport_header;
        new->inner_network_header = old->inner_network_header;
+       new->inner_mac_header = old->inner_mac_header;
        skb_dst_copy(new, old);
        new->rxhash             = old->rxhash;
        new->ooo_okay           = old->ooo_okay;
@@ -706,6 +735,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->tc_verd            = old->tc_verd;
 #endif
 #endif
+       new->vlan_proto         = old->vlan_proto;
        new->vlan_tci           = old->vlan_tci;
 
        skb_copy_secmark(new, old);
@@ -867,6 +897,18 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
 }
 EXPORT_SYMBOL(skb_clone);
 
+static void skb_headers_offset_update(struct sk_buff *skb, int off)
+{
+       /* {transport,network,mac}_header and tail are relative to skb->head */
+       skb->transport_header += off;
+       skb->network_header   += off;
+       if (skb_mac_header_was_set(skb))
+               skb->mac_header += off;
+       skb->inner_transport_header += off;
+       skb->inner_network_header += off;
+       skb->inner_mac_header += off;
+}
+
 static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 {
 #ifndef NET_SKBUFF_DATA_USES_OFFSET
@@ -879,13 +921,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        __copy_skb_header(new, old);
 
 #ifndef NET_SKBUFF_DATA_USES_OFFSET
-       /* {transport,network,mac}_header are relative to skb->head */
-       new->transport_header += offset;
-       new->network_header   += offset;
-       if (skb_mac_header_was_set(new))
-               new->mac_header       += offset;
-       new->inner_transport_header += offset;
-       new->inner_network_header   += offset;
+       skb_headers_offset_update(new, offset);
 #endif
        skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
        skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
@@ -1077,14 +1113,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 #else
        skb->end      = skb->head + size;
 #endif
-       /* {transport,network,mac}_header and tail are relative to skb->head */
        skb->tail             += off;
-       skb->transport_header += off;
-       skb->network_header   += off;
-       if (skb_mac_header_was_set(skb))
-               skb->mac_header += off;
-       skb->inner_transport_header += off;
-       skb->inner_network_header += off;
+       skb_headers_offset_update(skb, off);
        /* Only adjust this if it actually is csum_start rather than csum */
        if (skb->ip_summed == CHECKSUM_PARTIAL)
                skb->csum_start += nhead;
@@ -1180,12 +1210,7 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
        if (n->ip_summed == CHECKSUM_PARTIAL)
                n->csum_start += off;
 #ifdef NET_SKBUFF_DATA_USES_OFFSET
-       n->transport_header += off;
-       n->network_header   += off;
-       if (skb_mac_header_was_set(skb))
-               n->mac_header += off;
-       n->inner_transport_header += off;
-       n->inner_network_header    += off;
+       skb_headers_offset_update(n, off);
 #endif
 
        return n;
@@ -2741,12 +2766,19 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
        unsigned int tnl_hlen = skb_tnl_header_len(skb);
        unsigned int headroom;
        unsigned int len;
+       __be16 proto;
+       bool csum;
        int sg = !!(features & NETIF_F_SG);
        int nfrags = skb_shinfo(skb)->nr_frags;
        int err = -ENOMEM;
        int i = 0;
        int pos;
 
+       proto = skb_network_protocol(skb);
+       if (unlikely(!proto))
+               return ERR_PTR(-EINVAL);
+
+       csum = !!can_checksum_protocol(features, proto);
        __skb_push(skb, doffset);
        headroom = skb_headroom(skb);
        pos = skb_headlen(skb);
@@ -2884,6 +2916,12 @@ skip_fraglist:
                nskb->data_len = len - hsize;
                nskb->len += nskb->data_len;
                nskb->truesize += nskb->data_len;
+
+               if (!csum) {
+                       nskb->csum = skb_checksum(nskb, doffset,
+                                                 nskb->len - doffset, 0);
+                       nskb->ip_summed = CHECKSUM_NONE;
+               }
        } while ((offset += len) < skb->len);
 
        return segs;
@@ -3289,12 +3327,8 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
        if (!sk)
                return;
 
-       skb = skb_clone(orig_skb, GFP_ATOMIC);
-       if (!skb)
-               return;
-
        if (hwtstamps) {
-               *skb_hwtstamps(skb) =
+               *skb_hwtstamps(orig_skb) =
                        *hwtstamps;
        } else {
                /*
@@ -3302,9 +3336,13 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
                 * so keep the shared tx_flags and only
                 * store software time stamp
                 */
-               skb->tstamp = ktime_get_real();
+               orig_skb->tstamp = ktime_get_real();
        }
 
+       skb = skb_clone(orig_skb, GFP_ATOMIC);
+       if (!skb)
+               return;
+
        serr = SKB_EXT_ERR(skb);
        memset(serr, 0, sizeof(*serr));
        serr->ee.ee_errno = ENOMSG;
@@ -3361,6 +3399,7 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off)
        skb->ip_summed = CHECKSUM_PARTIAL;
        skb->csum_start = skb_headroom(skb) + start;
        skb->csum_offset = off;
+       skb_set_transport_header(skb, start);
        return true;
 }
 EXPORT_SYMBOL_GPL(skb_partial_csum_set);