]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/core/skbuff.c
udp: avoid a cache miss on dequeue
[linux.git] / net / core / skbuff.c
index 346d3e85dfbc2eca1ded0442ecb78d31e1768523..304602784c3b78249c73609e8aad9cbc55e70e66 100644 (file)
@@ -643,12 +643,10 @@ static void kfree_skbmem(struct sk_buff *skb)
        kmem_cache_free(skbuff_fclone_cache, fclones);
 }
 
-static void skb_release_head_state(struct sk_buff *skb)
+void skb_release_head_state(struct sk_buff *skb)
 {
        skb_dst_drop(skb);
-#ifdef CONFIG_XFRM
-       secpath_put(skb->sp);
-#endif
+       secpath_reset(skb);
        if (skb->destructor) {
                WARN_ON(in_irq());
                skb->destructor(skb);
@@ -694,12 +692,9 @@ EXPORT_SYMBOL(__kfree_skb);
  */
 void kfree_skb(struct sk_buff *skb)
 {
-       if (unlikely(!skb))
-               return;
-       if (likely(atomic_read(&skb->users) == 1))
-               smp_rmb();
-       else if (likely(!atomic_dec_and_test(&skb->users)))
+       if (!skb_unref(skb))
                return;
+
        trace_kfree_skb(skb, __builtin_return_address(0));
        __kfree_skb(skb);
 }
@@ -746,17 +741,32 @@ EXPORT_SYMBOL(skb_tx_error);
  */
 void consume_skb(struct sk_buff *skb)
 {
-       if (unlikely(!skb))
-               return;
-       if (likely(atomic_read(&skb->users) == 1))
-               smp_rmb();
-       else if (likely(!atomic_dec_and_test(&skb->users)))
+       if (!skb_unref(skb))
                return;
+
        trace_consume_skb(skb);
        __kfree_skb(skb);
 }
 EXPORT_SYMBOL(consume_skb);
 
+/**
+ *     consume_stateless_skb - free an skbuff, assuming it is stateless
+ *     @skb: buffer to free
+ *
+ *     Works like consume_skb(), but this variant assumes that all the head
+ *     states have been already dropped.
+ */
+void consume_stateless_skb(struct sk_buff *skb)
+{
+       if (!skb_unref(skb))
+               return;
+
+       trace_consume_skb(skb);
+       if (likely(skb->head))
+               skb_release_data(skb);
+       kfree_skbmem(skb);
+}
+
 void __kfree_skb_flush(void)
 {
        struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache);
@@ -2243,6 +2253,32 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
 }
 EXPORT_SYMBOL(skb_copy_and_csum_bits);
 
+static __wsum warn_crc32c_csum_update(const void *buff, int len, __wsum sum)
+{
+       net_warn_ratelimited(
+               "%s: attempt to compute crc32c without libcrc32c.ko\n",
+               __func__);
+       return 0;
+}
+
+static __wsum warn_crc32c_csum_combine(__wsum csum, __wsum csum2,
+                                      int offset, int len)
+{
+       net_warn_ratelimited(
+               "%s: attempt to compute crc32c without libcrc32c.ko\n",
+               __func__);
+       return 0;
+}
+
+static const struct skb_checksum_ops default_crc32c_ops = {
+       .update  = warn_crc32c_csum_update,
+       .combine = warn_crc32c_csum_combine,
+};
+
+const struct skb_checksum_ops *crc32c_csum_stub __read_mostly =
+       &default_crc32c_ops;
+EXPORT_SYMBOL(crc32c_csum_stub);
+
  /**
  *     skb_zerocopy_headlen - Calculate headroom needed for skb_zerocopy()
  *     @from: source buffer
@@ -2620,7 +2656,8 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len)
 {
        int pos = skb_headlen(skb);
 
-       skb_shinfo(skb1)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG;
+       skb_shinfo(skb1)->tx_flags |= skb_shinfo(skb)->tx_flags &
+                                     SKBTX_SHARED_FRAG;
        if (len < pos)  /* Split line is inside header. */
                skb_split_inside_header(skb, skb1, len, pos);
        else            /* Second chunk has no header, nothing to copy. */
@@ -3235,8 +3272,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
                skb_copy_from_linear_data_offset(head_skb, offset,
                                                 skb_put(nskb, hsize), hsize);
 
-               skb_shinfo(nskb)->tx_flags = skb_shinfo(head_skb)->tx_flags &
-                       SKBTX_SHARED_FRAG;
+               skb_shinfo(nskb)->tx_flags |= skb_shinfo(head_skb)->tx_flags &
+                                             SKBTX_SHARED_FRAG;
 
                while (pos < offset + len) {
                        if (i >= nfrags) {
@@ -3482,24 +3519,18 @@ void __init skb_init(void)
                                                NULL);
 }
 
-/**
- *     skb_to_sgvec - Fill a scatter-gather list from a socket buffer
- *     @skb: Socket buffer containing the buffers to be mapped
- *     @sg: The scatter-gather list to map into
- *     @offset: The offset into the buffer's contents to start mapping
- *     @len: Length of buffer space to be mapped
- *
- *     Fill the specified scatter-gather list with mappings/pointers into a
- *     region of the buffer space attached to a socket buffer.
- */
 static int
-__skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+__skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len,
+              unsigned int recursion_level)
 {
        int start = skb_headlen(skb);
        int i, copy = start - offset;
        struct sk_buff *frag_iter;
        int elt = 0;
 
+       if (unlikely(recursion_level >= 24))
+               return -EMSGSIZE;
+
        if (copy > 0) {
                if (copy > len)
                        copy = len;
@@ -3518,6 +3549,8 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
                end = start + skb_frag_size(&skb_shinfo(skb)->frags[i]);
                if ((copy = end - offset) > 0) {
                        skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+                       if (unlikely(elt && sg_is_last(&sg[elt - 1])))
+                               return -EMSGSIZE;
 
                        if (copy > len)
                                copy = len;
@@ -3532,16 +3565,22 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
        }
 
        skb_walk_frags(skb, frag_iter) {
-               int end;
+               int end, ret;
 
                WARN_ON(start > offset + len);
 
                end = start + frag_iter->len;
                if ((copy = end - offset) > 0) {
+                       if (unlikely(elt && sg_is_last(&sg[elt - 1])))
+                               return -EMSGSIZE;
+
                        if (copy > len)
                                copy = len;
-                       elt += __skb_to_sgvec(frag_iter, sg+elt, offset - start,
-                                             copy);
+                       ret = __skb_to_sgvec(frag_iter, sg+elt, offset - start,
+                                             copy, recursion_level + 1);
+                       if (unlikely(ret < 0))
+                               return ret;
+                       elt += ret;
                        if ((len -= copy) == 0)
                                return elt;
                        offset += copy;
@@ -3552,6 +3591,31 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
        return elt;
 }
 
+/**
+ *     skb_to_sgvec - Fill a scatter-gather list from a socket buffer
+ *     @skb: Socket buffer containing the buffers to be mapped
+ *     @sg: The scatter-gather list to map into
+ *     @offset: The offset into the buffer's contents to start mapping
+ *     @len: Length of buffer space to be mapped
+ *
+ *     Fill the specified scatter-gather list with mappings/pointers into a
+ *     region of the buffer space attached to a socket buffer. Returns either
+ *     the number of scatterlist items used, or -EMSGSIZE if the contents
+ *     could not fit.
+ */
+int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+{
+       int nsg = __skb_to_sgvec(skb, sg, offset, len, 0);
+
+       if (nsg <= 0)
+               return nsg;
+
+       sg_mark_end(&sg[nsg - 1]);
+
+       return nsg;
+}
+EXPORT_SYMBOL_GPL(skb_to_sgvec);
+
 /* As compared with skb_to_sgvec, skb_to_sgvec_nomark only map skb to given
  * sglist without mark the sg which contain last skb data as the end.
  * So the caller can mannipulate sg list as will when padding new data after
@@ -3574,19 +3638,11 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
 int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
                        int offset, int len)
 {
-       return __skb_to_sgvec(skb, sg, offset, len);
+       return __skb_to_sgvec(skb, sg, offset, len, 0);
 }
 EXPORT_SYMBOL_GPL(skb_to_sgvec_nomark);
 
-int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
-{
-       int nsg = __skb_to_sgvec(skb, sg, offset, len);
 
-       sg_mark_end(&sg[nsg - 1]);
-
-       return nsg;
-}
-EXPORT_SYMBOL_GPL(skb_to_sgvec);
 
 /**
  *     skb_cow_data - Check that a socket buffer's data buffers are writable
@@ -3754,8 +3810,11 @@ struct sk_buff *sock_dequeue_err_skb(struct sock *sk)
 
        spin_lock_irqsave(&q->lock, flags);
        skb = __skb_dequeue(q);
-       if (skb && (skb_next = skb_peek(q)))
+       if (skb && (skb_next = skb_peek(q))) {
                icmp_next = is_icmp_err_skb(skb_next);
+               if (icmp_next)
+                       sk->sk_err = SKB_EXT_ERR(skb_next)->ee.ee_origin;
+       }
        spin_unlock_irqrestore(&q->lock, flags);
 
        if (is_icmp_err_skb(skb) && !icmp_next)
@@ -3875,6 +3934,10 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb,
        if (!sk)
                return;
 
+       if (!hwtstamps && !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) &&
+           skb_shinfo(orig_skb)->tx_flags & SKBTX_IN_PROGRESS)
+               return;
+
        tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY;
        if (!skb_may_tx_timestamp(sk, tsonly))
                return;
@@ -3896,7 +3959,8 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb,
                return;
 
        if (tsonly) {
-               skb_shinfo(skb)->tx_flags = skb_shinfo(orig_skb)->tx_flags;
+               skb_shinfo(skb)->tx_flags |= skb_shinfo(orig_skb)->tx_flags &
+                                            SKBTX_ANY_TSTAMP;
                skb_shinfo(skb)->tskey = skb_shinfo(orig_skb)->tskey;
        }