svn commit: r194951 - in head/sys: fs/nfsclient net netinet netipsec nfsclient

Robert Watson rwatson at FreeBSD.org
Thu Jun 25 11:52:35 UTC 2009


Author: rwatson
Date: Thu Jun 25 11:52:33 2009
New Revision: 194951
URL: http://svn.freebsd.org/changeset/base/194951

Log:
  Add a new global rwlock, in_ifaddr_lock, which will synchronize use of the
  in_ifaddrhead and INADDR_HASH address lists.
  
  Previously, these lists were used unsynchronized as they were effectively
  never changed in steady state, but we've seen increasing reports of
  writer-writer races on very busy VPN servers as core count has gone up
  (and similar configurations where address lists change frequently and
  concurrently).
  
  For the time being, use rwlocks rather than rmlocks in order to take
  advantage of their better lock debugging support.  As a result, we don't
  enable ip_input()'s read-locking of INADDR_HASH until an rmlock conversion
  is complete and a performance analysis has been done.  This means that one
  class of reader-writer races still exists.
  
  MFC after:      6 weeks
  Reviewed by:    bz

Modified:
  head/sys/fs/nfsclient/nfs_clvnops.c
  head/sys/net/if_spppsubr.c
  head/sys/net/if_stf.c
  head/sys/netinet/if_ether.c
  head/sys/netinet/in.c
  head/sys/netinet/in_gif.c
  head/sys/netinet/in_mcast.c
  head/sys/netinet/in_pcb.c
  head/sys/netinet/in_var.h
  head/sys/netinet/ip_carp.c
  head/sys/netinet/ip_icmp.c
  head/sys/netinet/ip_input.c
  head/sys/netinet/raw_ip.c
  head/sys/netipsec/key.c
  head/sys/nfsclient/nfs_vnops.c

Modified: head/sys/fs/nfsclient/nfs_clvnops.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clvnops.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/fs/nfsclient/nfs_clvnops.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -1400,11 +1400,15 @@ again:
 	CURVNET_SET(P_TO_VNET(&proc0));
 #ifdef INET
 	INIT_VNET_INET(curvnet);
+	IN_IFADDR_RLOCK();
 	if (!TAILQ_EMPTY(&V_in_ifaddrhead))
 		cverf.lval[0] = IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))->sin_addr.s_addr;
 	else
 #endif
 		cverf.lval[0] = create_verf;
+#ifdef INET
+	IN_IFADDR_RUNLOCK();
+#endif
 	cverf.lval[1] = ++create_verf;
 	CURVNET_RESTORE();
 	error = nfsrpc_create(dvp, cnp->cn_nameptr, cnp->cn_namelen,

Modified: head/sys/net/if_spppsubr.c
==============================================================================
--- head/sys/net/if_spppsubr.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/net/if_spppsubr.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -4973,8 +4973,10 @@ sppp_set_ip_addr(struct sppp *sp, u_long
 		/* set new address */
 		si->sin_addr.s_addr = htonl(src);
 		ia = ifatoia(ifa);
+		IN_IFADDR_WLOCK();
 		LIST_REMOVE(ia, ia_hash);
 		LIST_INSERT_HEAD(INADDR_HASH(si->sin_addr.s_addr), ia, ia_hash);
+		IN_IFADDR_WUNLOCK();
 
 		/* add new route */
 		error = rtinit(ifa, (int)RTM_ADD, RTF_HOST);

Modified: head/sys/net/if_stf.c
==============================================================================
--- head/sys/net/if_stf.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/net/if_stf.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -620,15 +620,19 @@ stf_checkaddr4(sc, in, inifp)
 	/*
 	 * reject packets with broadcast
 	 */
+	IN_IFADDR_RLOCK();
 	for (ia4 = TAILQ_FIRST(&V_in_ifaddrhead);
 	     ia4;
 	     ia4 = TAILQ_NEXT(ia4, ia_link))
 	{
 		if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0)
 			continue;
-		if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr)
+		if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) {
+			IN_IFADDR_RUNLOCK();
 			return -1;
+		}
 	}
+	IN_IFADDR_RUNLOCK();
 
 	/*
 	 * perform ingress filter

Modified: head/sys/netinet/if_ether.c
==============================================================================
--- head/sys/netinet/if_ether.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/if_ether.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -509,11 +509,13 @@ in_arpinput(struct mbuf *m)
 	 * request for the virtual host ip.
 	 * XXX: This is really ugly!
 	 */
+	IN_IFADDR_RLOCK();
 	LIST_FOREACH(ia, INADDR_HASH(itaddr.s_addr), ia_hash) {
 		if (((bridged && ia->ia_ifp->if_bridge != NULL) ||
 		    ia->ia_ifp == ifp) &&
 		    itaddr.s_addr == ia->ia_addr.sin_addr.s_addr) {
 			ifa_ref(&ia->ia_ifa);
+			IN_IFADDR_RUNLOCK();
 			goto match;
 		}
 #ifdef DEV_CARP
@@ -522,6 +524,7 @@ in_arpinput(struct mbuf *m)
 		    itaddr.s_addr == ia->ia_addr.sin_addr.s_addr) {
 			carp_match = 1;
 			ifa_ref(&ia->ia_ifa);
+			IN_IFADDR_RUNLOCK();
 			goto match;
 		}
 #endif
@@ -531,6 +534,7 @@ in_arpinput(struct mbuf *m)
 		    ia->ia_ifp == ifp) &&
 		    isaddr.s_addr == ia->ia_addr.sin_addr.s_addr) {
 			ifa_ref(&ia->ia_ifa);
+			IN_IFADDR_RUNLOCK();
 			goto match;
 		}
 
@@ -549,11 +553,13 @@ in_arpinput(struct mbuf *m)
 			if (BDG_MEMBER_MATCHES_ARP(itaddr.s_addr, ifp, ia)) {
 				ifa_ref(&ia->ia_ifa);
 				ifp = ia->ia_ifp;
+				IN_IFADDR_RUNLOCK();
 				goto match;
 			}
 		}
 	}
 #undef BDG_MEMBER_MATCHES_ARP
+	IN_IFADDR_RUNLOCK();
 
 	/*
 	 * No match, use the first inet address on the receive interface
@@ -572,9 +578,13 @@ in_arpinput(struct mbuf *m)
 	/*
 	 * If bridging, fall back to using any inet address.
 	 */
-	if (!bridged || (ia = TAILQ_FIRST(&V_in_ifaddrhead)) == NULL)
+	IN_IFADDR_RLOCK();
+	if (!bridged || (ia = TAILQ_FIRST(&V_in_ifaddrhead)) == NULL) {
+		IN_IFADDR_RUNLOCK();
 		goto drop;
+	}
 	ifa_ref(&ia->ia_ifa);
+	IN_IFADDR_RUNLOCK();
 match:
 	if (!enaddr)
 		enaddr = (u_int8_t *)IF_LLADDR(ifp);

Modified: head/sys/netinet/in.c
==============================================================================
--- head/sys/netinet/in.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/in.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -100,15 +100,23 @@ in_localaddr(struct in_addr in)
 	register u_long i = ntohl(in.s_addr);
 	register struct in_ifaddr *ia;
 
+	IN_IFADDR_RLOCK();
 	if (V_subnetsarelocal) {
-		TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link)
-			if ((i & ia->ia_netmask) == ia->ia_net)
+		TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
+			if ((i & ia->ia_netmask) == ia->ia_net) {
+				IN_IFADDR_RUNLOCK();
 				return (1);
+			}
+		}
 	} else {
-		TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link)
-			if ((i & ia->ia_subnetmask) == ia->ia_subnet)
+		TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
+			if ((i & ia->ia_subnetmask) == ia->ia_subnet) {
+				IN_IFADDR_RUNLOCK();
 				return (1);
+			}
+		}
 	}
+	IN_IFADDR_RUNLOCK();
 	return (0);
 }
 
@@ -122,10 +130,14 @@ in_localip(struct in_addr in)
 	INIT_VNET_INET(curvnet);
 	struct in_ifaddr *ia;
 
+	IN_IFADDR_RLOCK();
 	LIST_FOREACH(ia, INADDR_HASH(in.s_addr), ia_hash) {
-		if (IA_SIN(ia)->sin_addr.s_addr == in.s_addr)
+		if (IA_SIN(ia)->sin_addr.s_addr == in.s_addr) {
+			IN_IFADDR_RUNLOCK();
 			return (1);
+		}
 	}
+	IN_IFADDR_RUNLOCK();
 	return (0);
 }
 
@@ -222,7 +234,7 @@ in_control(struct socket *so, u_long cmd
 	struct in_ifinfo *ii;
 	struct in_aliasreq *ifra = (struct in_aliasreq *)data;
 	struct sockaddr_in oldaddr;
-	int error, hostIsNew, iaIsNew, maskIsNew, s;
+	int error, hostIsNew, iaIsNew, maskIsNew;
 	int iaIsFirst;
 
 	ia = NULL;
@@ -313,6 +325,7 @@ in_control(struct socket *so, u_long cmd
 	 * first one on the interface, if possible.
 	 */
 	dst = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+	IN_IFADDR_RLOCK();
 	LIST_FOREACH(iap, INADDR_HASH(dst.s_addr), ia_hash) {
 		if (iap->ia_ifp == ifp &&
 		    iap->ia_addr.sin_addr.s_addr == dst.s_addr) {
@@ -324,6 +337,7 @@ in_control(struct socket *so, u_long cmd
 	}
 	if (ia != NULL)
 		ifa_ref(&ia->ia_ifa);
+	IN_IFADDR_RUNLOCK();
 	if (ia == NULL) {
 		IF_ADDR_LOCK(ifp);
 		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
@@ -351,6 +365,7 @@ in_control(struct socket *so, u_long cmd
 		if (ifra->ifra_addr.sin_family == AF_INET) {
 			struct in_ifaddr *oia;
 
+			IN_IFADDR_RLOCK();
 			for (oia = ia; ia; ia = TAILQ_NEXT(ia, ia_link)) {
 				if (ia->ia_ifp == ifp  &&
 				    ia->ia_addr.sin_addr.s_addr ==
@@ -361,6 +376,7 @@ in_control(struct socket *so, u_long cmd
 				ifa_ref(&ia->ia_ifa);
 			if (oia != NULL && ia != oia)
 				ifa_free(&oia->ia_ifa);
+			IN_IFADDR_RUNLOCK();
 			if ((ifp->if_flags & IFF_POINTOPOINT)
 			    && (cmd == SIOCAIFADDR)
 			    && (ifra->ifra_dstaddr.sin_addr.s_addr
@@ -405,9 +421,9 @@ in_control(struct socket *so, u_long cmd
 			TAILQ_INSERT_TAIL(&ifp->if_addrhead, ifa, ifa_link);
 			IF_ADDR_UNLOCK(ifp);
 			ifa_ref(ifa);			/* in_ifaddrhead */
-			s = splnet();
+			IN_IFADDR_WLOCK();
 			TAILQ_INSERT_TAIL(&V_in_ifaddrhead, ia, ia_link);
-			splx(s);
+			IN_IFADDR_WUNLOCK();
 			iaIsNew = 1;
 		}
 		break;
@@ -578,13 +594,14 @@ in_control(struct socket *so, u_long cmd
 	TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
 	IF_ADDR_UNLOCK(ifp);
 	ifa_free(&ia->ia_ifa);				/* if_addrhead */
-	s = splnet();
+
+	IN_IFADDR_WLOCK();
 	TAILQ_REMOVE(&V_in_ifaddrhead, ia, ia_link);
-	ifa_free(&ia->ia_ifa);				/* in_ifaddrhead */
 	if (ia->ia_addr.sin_family == AF_INET) {
 		struct in_ifaddr *if_ia;
 
 		LIST_REMOVE(ia, ia_hash);
+		IN_IFADDR_WUNLOCK();
 		/*
 		 * If this is the last IPv4 address configured on this
 		 * interface, leave the all-hosts group.
@@ -603,8 +620,9 @@ in_control(struct socket *so, u_long cmd
 			IN_MULTI_UNLOCK();
 		} else
 			ifa_free(&if_ia->ia_ifa);
-	}
-	splx(s);
+	} else
+		IN_IFADDR_WUNLOCK();
+	ifa_free(&ia->ia_ifa);				/* in_ifaddrhead */
 out:
 	if (ia != NULL)
 		ifa_free(&ia->ia_ifa);
@@ -811,9 +829,12 @@ in_ifinit(struct ifnet *ifp, struct in_i
 	if (oldaddr.sin_family == AF_INET)
 		LIST_REMOVE(ia, ia_hash);
 	ia->ia_addr = *sin;
-	if (ia->ia_addr.sin_family == AF_INET)
+	if (ia->ia_addr.sin_family == AF_INET) {
+		IN_IFADDR_WLOCK();
 		LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr),
 		    ia, ia_hash);
+		IN_IFADDR_WUNLOCK();
+	}
 	/*
 	 * Give the interface a chance to initialize
 	 * if this is its first address,
@@ -825,6 +846,7 @@ in_ifinit(struct ifnet *ifp, struct in_i
 			splx(s);
 			/* LIST_REMOVE(ia, ia_hash) is done in in_control */
 			ia->ia_addr = oldaddr;
+			IN_IFADDR_WLOCK();
 			if (ia->ia_addr.sin_family == AF_INET)
 				LIST_INSERT_HEAD(INADDR_HASH(
 				    ia->ia_addr.sin_addr.s_addr), ia, ia_hash);
@@ -836,6 +858,7 @@ in_ifinit(struct ifnet *ifp, struct in_i
 				 * with bogus ia entries in hash
 				 */
 				LIST_REMOVE(ia, ia_hash);
+			IN_IFADDR_WUNLOCK();
 			return (error);
 		}
 	}
@@ -943,6 +966,7 @@ in_addprefix(struct in_ifaddr *target, i
 		prefix.s_addr &= mask.s_addr;
 	}
 
+	IN_IFADDR_RLOCK();
 	TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 		if (rtinitflags(ia)) {
 			p = ia->ia_addr.sin_addr;
@@ -966,12 +990,16 @@ in_addprefix(struct in_ifaddr *target, i
 		if (ia->ia_flags & IFA_ROUTE) {
 			if (V_sameprefixcarponly &&
 			    target->ia_ifp->if_type != IFT_CARP &&
-			    ia->ia_ifp->if_type != IFT_CARP)
+			    ia->ia_ifp->if_type != IFT_CARP) {
+				IN_IFADDR_RUNLOCK();
 				return (EEXIST);
-			else
+			} else {
+				IN_IFADDR_RUNLOCK();
 				return (0);
+			}
 		}
 	}
+	IN_IFADDR_RUNLOCK();
 
 	/*
 	 * No-one seem to have this prefix route, so we try to insert it.
@@ -1031,6 +1059,7 @@ in_scrubprefix(struct in_ifaddr *target)
 		arp_ifscrub(target->ia_ifp, IA_SIN(target)->sin_addr.s_addr);
 	}
 
+	IN_IFADDR_RLOCK();
 	TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 		if (rtinitflags(ia))
 			p = ia->ia_dstaddr.sin_addr;
@@ -1054,6 +1083,7 @@ in_scrubprefix(struct in_ifaddr *target)
 		    && (ia->ia_ifp->if_type != IFT_CARP)
 #endif
 							) {
+			IN_IFADDR_RUNLOCK();
 			rtinit(&(target->ia_ifa), (int)RTM_DELETE,
 			    rtinitflags(target));
 			target->ia_flags &= ~IFA_ROUTE;
@@ -1065,6 +1095,7 @@ in_scrubprefix(struct in_ifaddr *target)
 			return (error);
 		}
 	}
+	IN_IFADDR_RUNLOCK();
 
 	/*
 	 * remove all L2 entries on the given prefix

Modified: head/sys/netinet/in_gif.c
==============================================================================
--- head/sys/netinet/in_gif.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/in_gif.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -387,13 +387,19 @@ gif_validate4(const struct ip *ip, struc
 	case 0: case 127: case 255:
 		return 0;
 	}
+
 	/* reject packets with broadcast on source */
+	/* XXXRW: should use hash lists? */
+	IN_IFADDR_RLOCK();
 	TAILQ_FOREACH(ia4, &V_in_ifaddrhead, ia_link) {
 		if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0)
 			continue;
-		if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr)
+		if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) {
+			IN_IFADDR_RUNLOCK();
 			return 0;
+		}
 	}
+	IN_IFADDR_RUNLOCK();
 
 	/* ingress filters on outer source */
 	if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) {

Modified: head/sys/netinet/in_mcast.c
==============================================================================
--- head/sys/netinet/in_mcast.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/in_mcast.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -1834,6 +1834,7 @@ inp_lookup_mcast_ifp(const struct inpcb 
 			struct ifnet *mifp;
 
 			mifp = NULL;
+			IN_IFADDR_RLOCK();
 			TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 				mifp = ia->ia_ifp;
 				if (!(mifp->if_flags & IFF_LOOPBACK) &&
@@ -1842,6 +1843,7 @@ inp_lookup_mcast_ifp(const struct inpcb 
 					break;
 				}
 			}
+			IN_IFADDR_RUNLOCK();
 		}
 	}
 

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/in_pcb.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -813,16 +813,21 @@ in_pcbconnect_setup(struct inpcb *inp, s
 		 * choose the broadcast address for that interface.
 		 */
 		if (faddr.s_addr == INADDR_ANY) {
+			IN_IFADDR_RLOCK();
 			faddr =
 			    IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))->sin_addr;
+			IN_IFADDR_RUNLOCK();
 			if (cred != NULL &&
 			    (error = prison_get_ip4(cred, &faddr)) != 0)
 				return (error);
-		} else if (faddr.s_addr == (u_long)INADDR_BROADCAST &&
-		    (TAILQ_FIRST(&V_in_ifaddrhead)->ia_ifp->if_flags &
-		    IFF_BROADCAST))
-			faddr = satosin(&TAILQ_FIRST(
-			    &V_in_ifaddrhead)->ia_broadaddr)->sin_addr;
+		} else if (faddr.s_addr == (u_long)INADDR_BROADCAST) {
+			IN_IFADDR_RLOCK();
+			if (TAILQ_FIRST(&V_in_ifaddrhead)->ia_ifp->if_flags &
+			    IFF_BROADCAST)
+				faddr = satosin(&TAILQ_FIRST(
+				    &V_in_ifaddrhead)->ia_broadaddr)->sin_addr;
+			IN_IFADDR_RUNLOCK();
+		}
 	}
 	if (laddr.s_addr == INADDR_ANY) {
 		error = in_pcbladdr(inp, &faddr, &laddr, cred);
@@ -842,12 +847,16 @@ in_pcbconnect_setup(struct inpcb *inp, s
 			imo = inp->inp_moptions;
 			if (imo->imo_multicast_ifp != NULL) {
 				ifp = imo->imo_multicast_ifp;
+				IN_IFADDR_RLOCK();
 				TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link)
 					if (ia->ia_ifp == ifp)
 						break;
-				if (ia == NULL)
+				if (ia == NULL) {
+					IN_IFADDR_RUNLOCK();
 					return (EADDRNOTAVAIL);
+				}
 				laddr = ia->ia_addr.sin_addr;
+				IN_IFADDR_RUNLOCK();
 			}
 		}
 	}

Modified: head/sys/netinet/in_var.h
==============================================================================
--- head/sys/netinet/in_var.h	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/in_var.h	Thu Jun 25 11:52:33 2009	(r194951)
@@ -114,6 +114,17 @@ extern	u_long in_ifaddrhmask;			/* mask 
 #define INADDR_HASH(x) \
 	(&V_in_ifaddrhashtbl[INADDR_HASHVAL(x) & V_in_ifaddrhmask])
 
+extern	struct rwlock in_ifaddr_lock;
+
+#define	IN_IFADDR_LOCK_INIT()	rw_init(&in_ifaddr_lock, "in_ifaddr_lock")
+#define	IN_IFADDR_LOCK_ASSERT()	rw_assert(&in_ifaddr_lock, RA_LOCKED)
+#define	IN_IFADDR_RLOCK()	rw_rlock(&in_ifaddr_lock)
+#define	IN_IFADDR_RLOCK_ASSERT()	rw_assert(&in_ifaddr_lock, RA_RLOCKED)
+#define	IN_IFADDR_RUNLOCK()	rw_runlock(&in_ifaddr_lock)
+#define	IN_IFADDR_WLOCK()	rw_wlock(&in_ifaddr_lock)
+#define	IN_IFADDR_WLOCK_ASSERT()	rw_assert(&in_ifaddr_lock, RA_WLOCKED)
+#define	IN_IFADDR_WUNLOCK()	rw_wunlock(&in_ifaddr_lock)
+
 /*
  * Macro for finding the internet address structure (in_ifaddr)
  * corresponding to one of our IP addresses (in_addr).

Modified: head/sys/netinet/ip_carp.c
==============================================================================
--- head/sys/netinet/ip_carp.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/ip_carp.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -1500,6 +1500,7 @@ carp_set_addr(struct carp_softc *sc, str
 
 	/* we have to do it by hands to check we won't match on us */
 	ia_if = NULL; own = 0;
+	IN_IFADDR_RLOCK();
 	TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 		/* and, yeah, we need a multicast-capable iface too */
 		if (ia->ia_ifp != SC2IFP(sc) &&
@@ -1513,20 +1514,30 @@ carp_set_addr(struct carp_softc *sc, str
 		}
 	}
 
-	if (!ia_if)
+	if (!ia_if) {
+		IN_IFADDR_RUNLOCK();
 		return (EADDRNOTAVAIL);
+	}
 
 	ia = ia_if;
+	ifa_ref(&ia->ia_ifa);
+	IN_IFADDR_RUNLOCK();
+
 	ifp = ia->ia_ifp;
 
 	if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 ||
-	    (imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp))
+	    (imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp)) {
+		ifa_free(&ia->ia_ifa);
 		return (EADDRNOTAVAIL);
+	}
 
 	if (imo->imo_num_memberships == 0) {
 		addr.s_addr = htonl(INADDR_CARP_GROUP);
-		if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) == NULL)
+		if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) ==
+		    NULL) {
+			ifa_free(&ia->ia_ifa);
 			return (ENOBUFS);
+		}
 		imo->imo_num_memberships++;
 		imo->imo_multicast_ifp = ifp;
 		imo->imo_multicast_ttl = CARP_DFLTTL;
@@ -1601,11 +1612,13 @@ carp_set_addr(struct carp_softc *sc, str
 	carp_setrun(sc, 0);
 
 	CARP_UNLOCK(cif);
+	ifa_free(&ia->ia_ifa);	/* XXXRW: should hold reference for softc. */
 
 	return (0);
 
 cleanup:
 	in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
+	ifa_free(&ia->ia_ifa);
 	return (error);
 }
 

Modified: head/sys/netinet/ip_icmp.c
==============================================================================
--- head/sys/netinet/ip_icmp.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/ip_icmp.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -675,12 +675,16 @@ icmp_reflect(struct mbuf *m)
 	 * If the incoming packet was addressed directly to one of our
 	 * own addresses, use dst as the src for the reply.
 	 */
+	IN_IFADDR_RLOCK();
 	LIST_FOREACH(ia, INADDR_HASH(t.s_addr), ia_hash) {
 		if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr) {
 			t = IA_SIN(ia)->sin_addr;
+			IN_IFADDR_RUNLOCK();
 			goto match;
 		}
 	}
+	IN_IFADDR_RUNLOCK();
+
 	/*
 	 * If the incoming packet was addressed to one of our broadcast
 	 * addresses, use the first non-broadcast address which corresponds

Modified: head/sys/netinet/ip_input.c
==============================================================================
--- head/sys/netinet/ip_input.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/ip_input.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -117,6 +117,7 @@ static int	maxfragsperpacket;
 int	ipstealth;
 static int	nipq;	/* Total # of reass queues */
 #endif
+struct	rwlock in_ifaddr_lock;
 
 SYSCTL_V_INT(V_NET, vnet_inet, _net_inet_ip, IPCTL_FORWARDING,
     forwarding, CTLFLAG_RW, ipforwarding, 0,
@@ -325,6 +326,7 @@ ip_init(void)
 
 	TAILQ_INIT(&V_in_ifaddrhead);
 	V_in_ifaddrhashtbl = hashinit(INADDR_NHASH, M_IFADDR, &V_in_ifaddrhmask);
+	IN_IFADDR_LOCK_INIT();
 
 	/* Initialize IP reassembly queue. */
 	for (i = 0; i < IPREASS_NHASH; i++)
@@ -615,6 +617,7 @@ passin:
 	/*
 	 * Check for exact addresses in the hash bucket.
 	 */
+	/* IN_IFADDR_RLOCK(); */
 	LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {
 		/*
 		 * If the address matches, verify that the packet
@@ -624,9 +627,12 @@ passin:
 		if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr && 
 		    (!checkif || ia->ia_ifp == ifp)) {
 			ifa_ref(&ia->ia_ifa);
+			/* IN_IFADDR_RUNLOCK(); */
 			goto ours;
 		}
 	}
+	/* IN_IFADDR_RUNLOCK(); */
+
 	/*
 	 * Check for broadcast addresses.
 	 *

Modified: head/sys/netinet/raw_ip.c
==============================================================================
--- head/sys/netinet/raw_ip.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netinet/raw_ip.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -678,9 +678,12 @@ rip_ctlinput(int cmd, struct sockaddr *s
 
 	switch (cmd) {
 	case PRC_IFDOWN:
+		IN_IFADDR_RLOCK();
 		TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 			if (ia->ia_ifa.ifa_addr == sa
 			    && (ia->ia_flags & IFA_ROUTE)) {
+				ifa_ref(&ia->ia_ifa);
+				IN_IFADDR_RUNLOCK();
 				/*
 				 * in_ifscrub kills the interface route.
 				 */
@@ -692,18 +695,26 @@ rip_ctlinput(int cmd, struct sockaddr *s
 				 * routing process they will come back.
 				 */
 				in_ifadown(&ia->ia_ifa, 0);
+				ifa_free(&ia->ia_ifa);
 				break;
 			}
 		}
+		if (ia == NULL)		/* If ia matched, already unlocked. */
+			IN_IFADDR_RUNLOCK();
 		break;
 
 	case PRC_IFUP:
+		IN_IFADDR_RLOCK();
 		TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
 			if (ia->ia_ifa.ifa_addr == sa)
 				break;
 		}
-		if (ia == 0 || (ia->ia_flags & IFA_ROUTE))
+		if (ia == NULL || (ia->ia_flags & IFA_ROUTE)) {
+			IN_IFADDR_RUNLOCK();
 			return;
+		}
+		ifa_ref(&ia->ia_ifa);
+		IN_IFADDR_RUNLOCK();
 		flags = RTF_UP;
 		ifp = ia->ia_ifa.ifa_ifp;
 
@@ -714,6 +725,7 @@ rip_ctlinput(int cmd, struct sockaddr *s
 		err = rtinit(&ia->ia_ifa, RTM_ADD, flags);
 		if (err == 0)
 			ia->ia_flags |= IFA_ROUTE;
+		ifa_free(&ia->ia_ifa);
 		break;
 	}
 }

Modified: head/sys/netipsec/key.c
==============================================================================
--- head/sys/netipsec/key.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/netipsec/key.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -3939,6 +3939,7 @@ key_ismyaddr(sa)
 #ifdef INET
 	case AF_INET:
 		sin = (struct sockaddr_in *)sa;
+		IN_IFADDR_RLOCK();
 		for (ia = V_in_ifaddrhead.tqh_first; ia;
 		     ia = ia->ia_link.tqe_next)
 		{
@@ -3946,9 +3947,11 @@ key_ismyaddr(sa)
 			    sin->sin_len == ia->ia_addr.sin_len &&
 			    sin->sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr)
 			{
+				IN_IFADDR_RUNLOCK();
 				return 1;
 			}
 		}
+		IN_IFADDR_RUNLOCK();
 		break;
 #endif
 #ifdef INET6

Modified: head/sys/nfsclient/nfs_vnops.c
==============================================================================
--- head/sys/nfsclient/nfs_vnops.c	Thu Jun 25 10:07:21 2009	(r194950)
+++ head/sys/nfsclient/nfs_vnops.c	Thu Jun 25 11:52:33 2009	(r194951)
@@ -1553,11 +1553,15 @@ again:
 			tl = nfsm_build(u_int32_t *, NFSX_V3CREATEVERF);
 #ifdef INET
 			INIT_VNET_INET(curvnet);
+			IN_IFADDR_RLOCK();
 			if (!TAILQ_EMPTY(&V_in_ifaddrhead))
 				*tl++ = IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))->sin_addr.s_addr;
 			else
 #endif
 				*tl++ = create_verf;
+#ifdef INET
+			IN_IFADDR_RUNLOCK();
+#endif
 			*tl = ++create_verf;
 		} else {
 			*tl = txdr_unsigned(NFSV3CREATE_UNCHECKED);


More information about the svn-src-all mailing list