Commit a59322be authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[UDP]: Only increment counter on first peek/recv

The previous move of the the UDP inDatagrams counter caused each
peek of the same packet to be counted separately.  This may be
undesirable.

This patch fixes this by adding a bit to sk_buff to record whether
this packet has already been seen through skb_recv_datagram.  We
then only increment the counter when the packet is seen for the
first time.

The only dodgy part is the fact that skb_recv_datagram doesn't have
a good way of returning this new bit of information.  So I've added
a new function __skb_recv_datagram that does return this and made
skb_recv_datagram a wrapper around it.

The plan is to eventually replace all uses of skb_recv_datagram with
this new function at which time it can be renamed its proper name.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1781f7f5
...@@ -288,6 +288,7 @@ struct sk_buff { ...@@ -288,6 +288,7 @@ struct sk_buff {
__u8 pkt_type:3, __u8 pkt_type:3,
fclone:2, fclone:2,
ipvs_property:1, ipvs_property:1,
peeked:1,
nf_trace:1; nf_trace:1;
__be16 protocol; __be16 protocol;
...@@ -1538,6 +1539,8 @@ static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len) ...@@ -1538,6 +1539,8 @@ static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
skb = skb->prev) 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, extern struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
int noblock, int *err); int noblock, int *err);
extern unsigned int datagram_poll(struct file *file, struct socket *sock, extern unsigned int datagram_poll(struct file *file, struct socket *sock,
......
...@@ -115,10 +115,10 @@ static int wait_for_packet(struct sock *sk, int *err, long *timeo_p) ...@@ -115,10 +115,10 @@ static int wait_for_packet(struct sock *sk, int *err, long *timeo_p)
} }
/** /**
* skb_recv_datagram - Receive a datagram skbuff * __skb_recv_datagram - Receive a datagram skbuff
* @sk: socket * @sk: socket
* @flags: MSG_ flags * @flags: MSG_ flags
* @noblock: blocking operation? * @peeked: returns non-zero if this packet has been seen before
* @err: error code returned * @err: error code returned
* *
* Get a datagram skbuff, understands the peeking, nonblocking wakeups * Get a datagram skbuff, understands the peeking, nonblocking wakeups
...@@ -143,8 +143,8 @@ static int wait_for_packet(struct sock *sk, int *err, long *timeo_p) ...@@ -143,8 +143,8 @@ static int wait_for_packet(struct sock *sk, int *err, long *timeo_p)
* quite explicitly by POSIX 1003.1g, don't change them without having * quite explicitly by POSIX 1003.1g, don't change them without having
* the standard around please. * the standard around please.
*/ */
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
int noblock, int *err) int *peeked, int *err)
{ {
struct sk_buff *skb; struct sk_buff *skb;
long timeo; long timeo;
...@@ -156,7 +156,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, ...@@ -156,7 +156,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
if (error) if (error)
goto no_packet; goto no_packet;
timeo = sock_rcvtimeo(sk, noblock); timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
do { do {
/* Again only user level code calls this function, so nothing /* 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, ...@@ -165,18 +165,19 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
* Look at current nfs client by the way... * Look at current nfs client by the way...
* However, this function was corrent in any case. 8) * However, this function was corrent in any case. 8)
*/ */
if (flags & MSG_PEEK) {
unsigned long cpu_flags; unsigned long cpu_flags;
spin_lock_irqsave(&sk->sk_receive_queue.lock, spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
cpu_flags);
skb = skb_peek(&sk->sk_receive_queue); skb = skb_peek(&sk->sk_receive_queue);
if (skb) if (skb) {
*peeked = skb->peeked;
if (flags & MSG_PEEK) {
skb->peeked = 1;
atomic_inc(&skb->users); atomic_inc(&skb->users);
spin_unlock_irqrestore(&sk->sk_receive_queue.lock,
cpu_flags);
} else } else
skb = skb_dequeue(&sk->sk_receive_queue); __skb_unlink(skb, &sk->sk_receive_queue);
}
spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
if (skb) if (skb)
return skb; return skb;
...@@ -194,6 +195,16 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, ...@@ -194,6 +195,16 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
*err = error; *err = error;
return NULL; 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) void skb_free_datagram(struct sock *sk, struct sk_buff *skb)
{ {
......
...@@ -827,6 +827,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ...@@ -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 sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
struct sk_buff *skb; struct sk_buff *skb;
unsigned int ulen, copied; unsigned int ulen, copied;
int peeked;
int err; int err;
int is_udplite = IS_UDPLITE(sk); int is_udplite = IS_UDPLITE(sk);
...@@ -840,7 +841,8 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ...@@ -840,7 +841,8 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
return ip_recv_error(sk, msg, len); return ip_recv_error(sk, msg, len);
try_again: try_again:
skb = skb_recv_datagram(sk, flags, noblock, &err); skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
&peeked, &err);
if (!skb) if (!skb)
goto out; goto out;
...@@ -875,6 +877,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ...@@ -875,6 +877,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (err) if (err)
goto out_free; goto out_free;
if (!peeked)
UDP_INC_STATS_USER(UDP_MIB_INDATAGRAMS, is_udplite); UDP_INC_STATS_USER(UDP_MIB_INDATAGRAMS, is_udplite);
sock_recv_timestamp(msg, sk, skb); sock_recv_timestamp(msg, sk, skb);
......
...@@ -123,6 +123,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, ...@@ -123,6 +123,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
struct sk_buff *skb; struct sk_buff *skb;
unsigned int ulen, copied; unsigned int ulen, copied;
int peeked;
int err; int err;
int is_udplite = IS_UDPLITE(sk); int is_udplite = IS_UDPLITE(sk);
...@@ -133,7 +134,8 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, ...@@ -133,7 +134,8 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
return ipv6_recv_error(sk, msg, len); return ipv6_recv_error(sk, msg, len);
try_again: try_again:
skb = skb_recv_datagram(sk, flags, noblock, &err); skb = __skb_recv_datagram(sk, flags | (noblock ? MSG_DONTWAIT : 0),
&peeked, &err);
if (!skb) if (!skb)
goto out; goto out;
...@@ -166,6 +168,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, ...@@ -166,6 +168,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
if (err) if (err)
goto out_free; goto out_free;
if (!peeked)
UDP6_INC_STATS_USER(UDP_MIB_INDATAGRAMS, is_udplite); UDP6_INC_STATS_USER(UDP_MIB_INDATAGRAMS, is_udplite);
sock_recv_timestamp(msg, sk, skb); sock_recv_timestamp(msg, sk, skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment