summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/skbuff.h3
-rw-r--r--net/core/datagram.c43
-rw-r--r--net/ipv4/udp.c7
-rw-r--r--net/ipv6/udp.c7
4 files changed, 40 insertions, 20 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 17b3f70fbbc3..c618fbf7d173 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -288,6 +288,7 @@ struct sk_buff {
__u8 pkt_type:3,
fclone:2,
ipvs_property:1,
+ peeked:1,
nf_trace:1;
__be16 protocol;
@@ -1538,6 +1539,8 @@ static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
skb = skb->prev)
+extern struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
+ int *peeked, int *err);
extern struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
int noblock, int *err);
extern unsigned int datagram_poll(struct file *file, struct socket *sock,
diff --git a/net/core/datagram.c b/net/core/datagram.c
index fbd6c76436d0..2d733131d7ce 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -115,10 +115,10 @@ out_noerr:
}
/**
- * skb_recv_datagram - Receive a datagram skbuff
+ * __skb_recv_datagram - Receive a datagram skbuff
* @sk: socket
* @flags: MSG_ flags
- * @noblock: blocking operation?
+ * @peeked: returns non-zero if this packet has been seen before
* @err: error code returned
*
* Get a datagram skbuff, understands the peeking, nonblocking wakeups
@@ -143,8 +143,8 @@ out_noerr:
* quite explicitly by POSIX 1003.1g, don't change them without having
* the standard around please.
*/
-struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
- int noblock, int *err)
+struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
+ int *peeked, int *err)
{
struct sk_buff *skb;
long timeo;
@@ -156,7 +156,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
if (error)
goto no_packet;
- timeo = sock_rcvtimeo(sk, noblock);
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
do {
/* Again only user level code calls this function, so nothing
@@ -165,18 +165,19 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
* Look at current nfs client by the way...
* However, this function was corrent in any case. 8)
*/
- if (flags & MSG_PEEK) {
- unsigned long cpu_flags;
-
- spin_lock_irqsave(&sk->sk_receive_queue.lock,
- cpu_flags);
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb)
+ unsigned long cpu_flags;
+
+ spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb) {
+ *peeked = skb->peeked;
+ if (flags & MSG_PEEK) {
+ skb->peeked = 1;
atomic_inc(&skb->users);
- spin_unlock_irqrestore(&sk->sk_receive_queue.lock,
- cpu_flags);
- } else
- skb = skb_dequeue(&sk->sk_receive_queue);
+ } else
+ __skb_unlink(skb, &sk->sk_receive_queue);
+ }
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
if (skb)
return skb;
@@ -194,6 +195,16 @@ no_packet:
*err = error;
return NULL;
}
+EXPORT_SYMBOL(__skb_recv_datagram);
+
+struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
+ int noblock, int *err)
+{
+ int peeked;
+
+ return __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
+ &peeked, err);
+}
void skb_free_datagram(struct sock *sk, struct sk_buff *skb)
{
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 78cfcb4a1b3f..9ed6393c65d9 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -827,6 +827,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
struct sk_buff *skb;
unsigned int ulen, copied;
+ int peeked;
int err;
int is_udplite = IS_UDPLITE(sk);
@@ -840,7 +841,8 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
return ip_recv_error(sk, msg, len);
try_again:
- skb = skb_recv_datagram(sk, flags, noblock, &err);
+ skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
+ &peeked, &err);
if (!skb)
goto out;
@@ -875,7 +877,8 @@ try_again:
if (err)
goto out_free;
- UDP_INC_STATS_USER(UDP_MIB_INDATAGRAMS, is_udplite);
+ if (!peeked)
+ UDP_INC_STATS_USER(UDP_MIB_INDATAGRAMS, is_udplite);
sock_recv_timestamp(msg, sk, skb);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 36bdcd2e1b52..fa640765385e 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -123,6 +123,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
struct inet_sock *inet = inet_sk(sk);
struct sk_buff *skb;
unsigned int ulen, copied;
+ int peeked;
int err;
int is_udplite = IS_UDPLITE(sk);
@@ -133,7 +134,8 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
return ipv6_recv_error(sk, msg, len);
try_again:
- skb = skb_recv_datagram(sk, flags, noblock, &err);
+ skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
+ &peeked, &err);
if (!skb)
goto out;
@@ -166,7 +168,8 @@ try_again:
if (err)
goto out_free;
- UDP6_INC_STATS_USER(UDP_MIB_INDATAGRAMS, is_udplite);
+ if (!peeked)
+ UDP6_INC_STATS_USER(UDP_MIB_INDATAGRAMS, is_udplite);
sock_recv_timestamp(msg, sk, skb);