patch-2.1.90 linux/net/ipv6/udp.c

Next file: linux/net/ipx/af_ipx.c
Previous file: linux/net/ipv6/tcp_ipv6.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.89/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.21 1997/12/29 19:52:52 kuznet Exp $
+ *	$Id: udp.c,v 1.24 1998/03/12 03:20:21 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -448,32 +448,43 @@
 	return NULL;
 }
 
+/*
+ * Note: called only from the BH handler context,
+ * so we don't need to lock the hashes.
+ */
 static void udpv6_mcast_deliver(struct udphdr *uh,
 				struct in6_addr *saddr, struct in6_addr *daddr,
 				struct sk_buff *skb)
 {
 	struct sock *sk, *sk2;
+	struct sk_buff *buff;
 
-	SOCKHASH_LOCK();
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
 	sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
-	if(sk) {
-		sk2 = sk;
-		while((sk2 = udp_v6_mcast_next(sk2->next,
-					       uh->dest, saddr,
-					       uh->source, daddr))) {
-			struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
-			if (buff && sock_queue_rcv_skb(sk2, buff) < 0) {
-				buff->sk = NULL;
-				kfree_skb(buff);
-			}
+	if (!sk)
+		goto free_skb;
+
+	buff = NULL;
+	sk2 = sk;
+	while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr,
+						  uh->source, daddr))) {
+		if (!buff) {
+			buff = skb_clone(skb, GFP_ATOMIC);
+			if (!buff)
+				continue;
 		}
+		if (sock_queue_rcv_skb(sk2, buff) >= 0)
+			buff = NULL;
 	}
-	if(!sk || sock_queue_rcv_skb(sk, skb) < 0) {
+	if (buff) {
+		buff->sk = NULL;
+		kfree_skb(buff);
+	}
+	if (sock_queue_rcv_skb(sk, skb) < 0) {
+	free_skb:
 		skb->sk = NULL;
 		kfree_skb(skb);
 	}
-	SOCKHASH_UNLOCK();
 }
 
 int udpv6_rcv(struct sk_buff *skb, struct device *dev,

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov