[UDP]: Avoid repeated counting of checksum errors due to peeking
authorHerbert Xu <herbert@gondor.apana.org.au>
Wed, 5 Dec 2007 09:51:58 +0000 (01:51 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 28 Jan 2008 22:56:32 +0000 (14:56 -0800)
Currently it is possible for two processes to peek on the same socket
and end up incrementing the error counter twice for the same packet.

This patch fixes it by making skb_kill_datagram return whether it
succeeded in unlinking the packet and only incrementing the counter
if it did.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/skbuff.h
net/core/datagram.c
net/ipv4/udp.c
net/ipv6/udp.c

index d39f53ef66bb7c41927146f38b7cdd9fc1423f1e..17b3f70fbbc38454862a0bf2a4212fb894b26635 100644 (file)
@@ -1549,7 +1549,7 @@ extern int               skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
                                                        int hlen,
                                                        struct iovec *iov);
 extern void           skb_free_datagram(struct sock *sk, struct sk_buff *skb);
-extern void           skb_kill_datagram(struct sock *sk, struct sk_buff *skb,
+extern int            skb_kill_datagram(struct sock *sk, struct sk_buff *skb,
                                         unsigned int flags);
 extern __wsum         skb_checksum(const struct sk_buff *skb, int offset,
                                    int len, __wsum csum);
index 029b93e246b45ea934b1da0cd018425e52965fcb..fbd6c76436d062d1068425b6879d25c0f31c8c77 100644 (file)
@@ -217,20 +217,27 @@ void skb_free_datagram(struct sock *sk, struct sk_buff *skb)
  *     This function currently only disables BH when acquiring the
  *     sk_receive_queue lock.  Therefore it must not be used in a
  *     context where that lock is acquired in an IRQ context.
+ *
+ *     It returns 0 if the packet was removed by us.
  */
 
-void skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
+int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
 {
+       int err = 0;
+
        if (flags & MSG_PEEK) {
+               err = -ENOENT;
                spin_lock_bh(&sk->sk_receive_queue.lock);
                if (skb == skb_peek(&sk->sk_receive_queue)) {
                        __skb_unlink(skb, &sk->sk_receive_queue);
                        atomic_dec(&skb->users);
+                       err = 0;
                }
                spin_unlock_bh(&sk->sk_receive_queue.lock);
        }
 
        kfree_skb(skb);
+       return err;
 }
 
 EXPORT_SYMBOL(skb_kill_datagram);
index d0283b7fcec5e52923b7724049dacc9752513e40..f50de5d5218ddce45c578f6f05a78de5e13ea8c0 100644 (file)
@@ -899,9 +899,8 @@ out:
        return err;
 
 csum_copy_err:
-       UDP_INC_STATS_USER(UDP_MIB_INERRORS, is_udplite);
-
-       skb_kill_datagram(sk, skb, flags);
+       if (!skb_kill_datagram(sk, skb, flags))
+               UDP_INC_STATS_USER(UDP_MIB_INERRORS, is_udplite);
 
        if (noblock)
                return -EAGAIN;
index 77ab31b9923263e4a861a2f66f4f7842d3418f59..87bccec9882a4f8a4dad00d18897b09f1b088b71 100644 (file)
@@ -207,8 +207,8 @@ out:
        return err;
 
 csum_copy_err:
-       UDP6_INC_STATS_USER(UDP_MIB_INERRORS, is_udplite);
-       skb_kill_datagram(sk, skb, flags);
+       if (!skb_kill_datagram(sk, skb, flags))
+               UDP6_INC_STATS_USER(UDP_MIB_INERRORS, is_udplite);
 
        if (flags & MSG_DONTWAIT)
                return -EAGAIN;