diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d39f53ef66bb..17b3f70fbbc3 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -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); diff --git a/net/core/datagram.c b/net/core/datagram.c index 029b93e246b4..fbd6c76436d0 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -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); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index d0283b7fcec5..f50de5d5218d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -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; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 77ab31b99232..87bccec9882a 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -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;