diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d39f53ef66bb7c41927146f38b7cdd9fc1423f1e..17b3f70fbbc38454862a0bf2a4212fb894b26635 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 029b93e246b45ea934b1da0cd018425e52965fcb..fbd6c76436d062d1068425b6879d25c0f31c8c77 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 d0283b7fcec5e52923b7724049dacc9752513e40..f50de5d5218ddce45c578f6f05a78de5e13ea8c0 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -899,9 +899,8 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, 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 77ab31b9923263e4a861a2f66f4f7842d3418f59..87bccec9882a4f8a4dad00d18897b09f1b088b71 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -207,8 +207,8 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, 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;