From: Neil Horman <nhorman@redhat.com> Date: Thu, 26 Feb 2009 10:49:27 -0500 Subject: [net] fix a few udp counters Message-id: 20090226154927.GD24652@shamino.rdu.redhat.com O-Subject: [RHEL5.4 PATCH] net: fix a few udp counters (bz 483266) Bugzilla: 483266 RH-Acked-by: David Miller <davem@redhat.com> RH-Acked-by: Thomas Graf <tgraf@redhat.com> Hey all- This is a backport of the following upstream commits: a59322be07c964e916d15be3df473fb7ba20c41e f26ba1751145edbf52b2c89a40e389f2fbdfc1af 0856f93958c488f0cc656be53c26dfd20663bdb3 They prevent UDPInDataGrams and UDPIn6DataGrams from getting double counted when an applications reads a socket queue using MSG_PEEK and MSG_DONTWAIT. Fixes bz 483266 diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index cc82c01..d1b1c73 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -327,6 +327,10 @@ struct sk_buff { *data, *tail, *end; + /* Extra stuff at the end to avoid breaking abi */ +#ifndef __GENKSYMS__ + int peeked; +#endif }; #ifdef __KERNEL__ @@ -1422,6 +1426,8 @@ static inline void kunmap_skb_frag(void *vaddr) 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 6d8634d..e013026 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -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,25 @@ 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, + unsigned long cpu_flags; + + if (flags & MSG_PEEK) { + spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags); skb = skb_peek(&sk->sk_receive_queue); - if (skb) + if (skb) { + *peeked = skb->peeked; + skb->peeked = 1; atomic_inc(&skb->users); - spin_unlock_irqrestore(&sk->sk_receive_queue.lock, + } + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags); - } else + } else { skb = skb_dequeue(&sk->sk_receive_queue); + if (skb) + *peeked = skb->peeked; + } if (skb) return skb; @@ -195,6 +202,17 @@ no_packet: 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) { kfree_skb(skb); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 836cc5f..59e4d77 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -162,6 +162,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, goto nodata; memset(skb, 0, offsetof(struct sk_buff, truesize)); + skb->peeked = 0; skb->truesize = size + sizeof(struct sk_buff); atomic_set(&skb->users, 1); skb->head = data; @@ -234,6 +235,7 @@ struct sk_buff *alloc_skb_from_cache(kmem_cache_t *cp, memset(skb, 0, offsetof(struct sk_buff, truesize)); skb->truesize = size + sizeof(struct sk_buff); + skb->peeked = 0; /* Had to do this for ABI safety */ atomic_set(&skb->users, 1); skb->head = data; skb->data = data; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f83064a..7c36cc8 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -115,6 +115,7 @@ */ DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly; +EXPORT_SYMBOL(udp_statistics); struct hlist_head udp_hash[UDP_HTABLE_SIZE]; DEFINE_RWLOCK(udp_hash_lock); @@ -802,6 +803,7 @@ static 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; int copied, err; + int peeked; /* * Check any passed addresses @@ -813,7 +815,8 @@ static 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; @@ -841,7 +844,8 @@ try_again: if (err) goto out_free; - UDP_INC_STATS_USER(UDP_MIB_INDATAGRAMS); + if (!peeked) + UDP_INC_STATS_USER(UDP_MIB_INDATAGRAMS); sock_recv_timestamp(msg, sk, skb); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f6d36e0..f76f0a2 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -233,6 +233,8 @@ static int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, struct sk_buff *skb; size_t copied; int err; + int peeked; + int is_udp4; if (addr_len) *addr_len=sizeof(struct sockaddr_in6); @@ -241,7 +243,8 @@ static 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; @@ -251,6 +254,8 @@ try_again: msg->msg_flags |= MSG_TRUNC; } + is_udp4 = (skb->protocol == htons(ETH_P_IP)); + if (skb->ip_summed==CHECKSUM_UNNECESSARY) { err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, copied); @@ -267,7 +272,12 @@ try_again: if (err) goto out_free; - UDP6_INC_STATS_USER(UDP_MIB_INDATAGRAMS); + if (!peeked) { + if (is_udp4) + UDP_INC_STATS_USER(UDP_MIB_INDATAGRAMS); + else + UDP6_INC_STATS_USER(UDP_MIB_INDATAGRAMS); + } sock_recv_timestamp(msg, sk, skb); @@ -281,7 +291,7 @@ try_again: sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = 0; - if (skb->protocol == htons(ETH_P_IP)) + if (is_udp4) ipv6_addr_set(&sin6->sin6_addr, 0, 0, htonl(0xffff), skb->nh.iph->saddr); else { @@ -291,7 +301,7 @@ try_again: } } - if (skb->protocol == htons(ETH_P_IP)) { + if (is_udp4) { if (inet->cmsg_flags) ip_cmsg_recv(msg, skb); } else { @@ -316,7 +326,10 @@ csum_copy_err: release_sock(sk); if (flags & MSG_DONTWAIT) { - UDP6_INC_STATS_USER(UDP_MIB_INERRORS); + if (is_udp4) + UDP_INC_STATS_USER(UDP_MIB_INERRORS); + else + UDP6_INC_STATS_USER(UDP_MIB_INERRORS); return -EAGAIN; } goto try_again;