Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 2145

kernel-2.6.18-194.11.1.el5.src.rpm

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;