Module Name:    src
Committed By:   snj
Date:           Fri Nov 17 20:24:05 UTC 2017

Modified Files:
        src/sys/net [netbsd-8]: if_llatbl.c if_llatbl.h
        src/sys/netinet [netbsd-8]: if_arp.c in.c
        src/sys/netinet6 [netbsd-8]: in6.c nd6.c

Log Message:
Pull up following revision(s) (requested by ozaki-r in ticket #353):
        sys/net/if_llatbl.c: 1.22
        sys/net/if_llatbl.h: 1.13
        sys/netinet/if_arp.c: 1.254
        sys/netinet/in.c: 1.208-1.209
        sys/netinet6/in6.c: 1.249-1.250
        sys/netinet6/nd6.c: 1.237
Remove redundant KASSERTMSG
The function is static, has just one caller and the caller does the same check.
--
Fix a deadlock between a route update and lltable
It happens because rtalloc1 is called from lltable with holding
IF_AFDATA_WLOCK.
If a route update is in action, rtalloc1 would wait for its completion with
holding IF_AFDATA_WLOCK. At the same moment, a softint (e.g., arpintr) may try
to take IF_AFDATA_WLOCK and get stuck on it. Unfortunately the stuck softint
prevents the route update from progressing because the route update calls
psref_target_destroy that needs the softint to complete.
A resource allocation graph of the senario looks like this:
    route update =(psref_target_destroy)=> softint => IF_AFDATA_WLOCK
    =(rt_update_wait)=> route update
Fix the deadlock by pulling rtalloc1 out of the lltable codes inside
IF_AFDATA_WLOCK.
Note that the deadlock happens only if NET_MPSAFE is enabled.


To generate a diff of this commit:
cvs rdiff -u -r1.18.6.1 -r1.18.6.2 src/sys/net/if_llatbl.c
cvs rdiff -u -r1.10.8.1 -r1.10.8.2 src/sys/net/if_llatbl.h
cvs rdiff -u -r1.250.2.2 -r1.250.2.3 src/sys/netinet/if_arp.c
cvs rdiff -u -r1.203.2.1 -r1.203.2.2 src/sys/netinet/in.c
cvs rdiff -u -r1.245.2.1 -r1.245.2.2 src/sys/netinet6/in6.c
cvs rdiff -u -r1.232.2.2 -r1.232.2.3 src/sys/netinet6/nd6.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/if_llatbl.c
diff -u src/sys/net/if_llatbl.c:1.18.6.1 src/sys/net/if_llatbl.c:1.18.6.2
--- src/sys/net/if_llatbl.c:1.18.6.1	Fri Jul  7 13:57:26 2017
+++ src/sys/net/if_llatbl.c	Fri Nov 17 20:24:05 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_llatbl.c,v 1.18.6.1 2017/07/07 13:57:26 martin Exp $	*/
+/*	$NetBSD: if_llatbl.c,v 1.18.6.2 2017/11/17 20:24:05 snj Exp $	*/
 /*
  * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved.
  * Copyright (c) 2004-2008 Qing Li. All rights reserved.
@@ -386,7 +386,7 @@ llentry_alloc(struct ifnet *ifp, struct 
 	    (ifp->if_flags & IFF_NOARP) == 0) {
 #endif
 		IF_AFDATA_WLOCK(ifp);
-		la = lla_create(lt, 0, (struct sockaddr *)dst);
+		la = lla_create(lt, 0, (struct sockaddr *)dst, NULL /* XXX */);
 		IF_AFDATA_WUNLOCK(ifp);
 	}
 
@@ -656,7 +656,12 @@ lla_rt_output(const u_char rtm_type, con
 	error = 0;
 
 	switch (rtm_type) {
-	case RTM_ADD:
+	case RTM_ADD: {
+		struct rtentry *rt;
+
+		/* Never call rtalloc1 with IF_AFDATA_WLOCK */
+		rt = rtalloc1(dst, 0);
+
 		/* Add static LLE */
 		IF_AFDATA_WLOCK(ifp);
 		lle = lla_lookup(llt, 0, dst);
@@ -666,15 +671,19 @@ lla_rt_output(const u_char rtm_type, con
 		    (lle->la_flags & LLE_STATIC || lle->la_expire == 0)) {
 			LLE_RUNLOCK(lle);
 			IF_AFDATA_WUNLOCK(ifp);
+			if (rt != NULL)
+				rt_unref(rt);
 			error = EEXIST;
 			goto out;
 		}
 		if (lle != NULL)
 			LLE_RUNLOCK(lle);
 
-		lle = lla_create(llt, 0, dst);
+		lle = lla_create(llt, 0, dst, rt);
 		if (lle == NULL) {
 			IF_AFDATA_WUNLOCK(ifp);
+			if (rt != NULL)
+				rt_unref(rt);
 			error = ENOMEM;
 			goto out;
 		}
@@ -703,6 +712,8 @@ lla_rt_output(const u_char rtm_type, con
 		laflags = lle->la_flags;
 		LLE_WUNLOCK(lle);
 		IF_AFDATA_WUNLOCK(ifp);
+		if (rt != NULL)
+			rt_unref(rt);
 #if defined(INET) && NARP > 0
 		/* gratuitous ARP */
 		if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) {
@@ -721,8 +732,8 @@ lla_rt_output(const u_char rtm_type, con
 #else
 		(void)laflags;
 #endif
-
 		break;
+	    }
 
 	case RTM_DELETE:
 		IF_AFDATA_WLOCK(ifp);

Index: src/sys/net/if_llatbl.h
diff -u src/sys/net/if_llatbl.h:1.10.8.1 src/sys/net/if_llatbl.h:1.10.8.2
--- src/sys/net/if_llatbl.h:1.10.8.1	Fri Jul  7 13:57:26 2017
+++ src/sys/net/if_llatbl.h	Fri Nov 17 20:24:05 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_llatbl.h,v 1.10.8.1 2017/07/07 13:57:26 martin Exp $	*/
+/*	$NetBSD: if_llatbl.h,v 1.10.8.2 2017/11/17 20:24:05 snj Exp $	*/
 /*
  * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved.
  * Copyright (c) 2004-2008 Qing Li. All rights reserved.
@@ -190,7 +190,7 @@ struct llentry {
 typedef	struct llentry *(llt_lookup_t)(struct lltable *, u_int flags,
     const struct sockaddr *l3addr);
 typedef	struct llentry *(llt_create_t)(struct lltable *, u_int flags,
-    const struct sockaddr *l3addr);
+    const struct sockaddr *l3addr, const struct rtentry *);
 typedef	int (llt_delete_t)(struct lltable *, u_int flags,
     const struct sockaddr *l3addr);
 typedef void (llt_prefix_free_t)(struct lltable *,
@@ -297,10 +297,11 @@ lla_lookup(struct lltable *llt, u_int fl
 }
 
 static __inline struct llentry *
-lla_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
+lla_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr,
+    const struct rtentry *rt)
 {
 
-	return (llt->llt_create(llt, flags, l3addr));
+	return (llt->llt_create(llt, flags, l3addr, rt));
 }
 
 static __inline int

Index: src/sys/netinet/if_arp.c
diff -u src/sys/netinet/if_arp.c:1.250.2.2 src/sys/netinet/if_arp.c:1.250.2.3
--- src/sys/netinet/if_arp.c:1.250.2.2	Fri Jul  7 13:57:27 2017
+++ src/sys/netinet/if_arp.c	Fri Nov 17 20:24:05 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_arp.c,v 1.250.2.2 2017/07/07 13:57:27 martin Exp $	*/
+/*	$NetBSD: if_arp.c,v 1.250.2.3 2017/11/17 20:24:05 snj Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2000, 2008 The NetBSD Foundation, Inc.
@@ -68,7 +68,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.250.2.2 2017/07/07 13:57:27 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.250.2.3 2017/11/17 20:24:05 snj Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -754,10 +754,15 @@ notfound:
 	}
 #undef _IFF_NOARP
 	if (la == NULL) {
+		struct rtentry *_rt;
+
 		create_lookup = "create";
+		_rt = rtalloc1(dst, 0);
 		IF_AFDATA_WLOCK(ifp);
-		la = lla_create(LLTABLE(ifp), LLE_EXCLUSIVE, dst);
+		la = lla_create(LLTABLE(ifp), LLE_EXCLUSIVE, dst, _rt);
 		IF_AFDATA_WUNLOCK(ifp);
+		if (_rt != NULL)
+			rt_unref(_rt);
 		if (la == NULL)
 			ARP_STATINC(ARP_STAT_ALLOCFAIL);
 		else {
@@ -1452,9 +1457,14 @@ arpcreate(struct ifnet *ifp, struct mbuf
 	la = arplookup(ifp, m, addr, sa, wlock);
 
 	if (la == NULL) {
+		struct rtentry *rt;
+
+		rt = rtalloc1(sa, 0);
 		IF_AFDATA_WLOCK(ifp);
-		la = lla_create(LLTABLE(ifp), flags, sa);
+		la = lla_create(LLTABLE(ifp), flags, sa, rt);
 		IF_AFDATA_WUNLOCK(ifp);
+		if (rt != NULL)
+			rt_unref(rt);
 
 		if (la != NULL)
 			arp_init_llentry(ifp, la);

Index: src/sys/netinet/in.c
diff -u src/sys/netinet/in.c:1.203.2.1 src/sys/netinet/in.c:1.203.2.2
--- src/sys/netinet/in.c:1.203.2.1	Fri Jul  7 13:57:27 2017
+++ src/sys/netinet/in.c	Fri Nov 17 20:24:05 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in.c,v 1.203.2.1 2017/07/07 13:57:27 martin Exp $	*/
+/*	$NetBSD: in.c,v 1.203.2.2 2017/11/17 20:24:05 snj Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.203.2.1 2017/07/07 13:57:27 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.203.2.2 2017/11/17 20:24:05 snj Exp $");
 
 #include "arp.h"
 
@@ -1938,15 +1938,11 @@ in_lltable_free_entry(struct lltable *ll
 }
 
 static int
-in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr)
+in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr,
+    const struct rtentry *rt)
 {
-	struct rtentry *rt;
 	int error = EINVAL;
 
-	KASSERTMSG(l3addr->sa_family == AF_INET,
-	    "sin_family %d", l3addr->sa_family);
-
-	rt = rtalloc1(l3addr, 0);
 	if (rt == NULL)
 		return error;
 
@@ -2007,7 +2003,6 @@ in_lltable_rtcheck(struct ifnet *ifp, u_
 
 	error = 0;
 error:
-	rt_unref(rt);
 	return error;
 }
 
@@ -2098,7 +2093,8 @@ in_lltable_delete(struct lltable *llt, u
 }
 
 static struct llentry *
-in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
+in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3addr,
+    const struct rtentry *rt)
 {
 	const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
 	struct ifnet *ifp = llt->llt_ifp;
@@ -2123,7 +2119,7 @@ in_lltable_create(struct lltable *llt, u
 	 * verify this.
 	 */
 	if (!(flags & LLE_IFADDR) &&
-	    in_lltable_rtcheck(ifp, flags, l3addr) != 0)
+	    in_lltable_rtcheck(ifp, flags, l3addr, rt) != 0)
 		return (NULL);
 
 	lle = in_lltable_new(sin->sin_addr, flags);

Index: src/sys/netinet6/in6.c
diff -u src/sys/netinet6/in6.c:1.245.2.1 src/sys/netinet6/in6.c:1.245.2.2
--- src/sys/netinet6/in6.c:1.245.2.1	Fri Jul  7 13:57:26 2017
+++ src/sys/netinet6/in6.c	Fri Nov 17 20:24:05 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6.c,v 1.245.2.1 2017/07/07 13:57:26 martin Exp $	*/
+/*	$NetBSD: in6.c,v 1.245.2.2 2017/11/17 20:24:05 snj Exp $	*/
 /*	$KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.245.2.1 2017/07/07 13:57:26 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.245.2.2 2017/11/17 20:24:05 snj Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -2458,17 +2458,11 @@ in6_lltable_free_entry(struct lltable *l
 }
 
 static int
-in6_lltable_rtcheck(struct ifnet *ifp,
-		    u_int flags,
-		    const struct sockaddr *l3addr)
+in6_lltable_rtcheck(struct ifnet *ifp, u_int flags,
+    const struct sockaddr *l3addr, const struct rtentry *rt)
 {
-	struct rtentry *rt;
 	char ip6buf[INET6_ADDRSTRLEN];
 
-	KASSERTMSG(l3addr->sa_family == AF_INET6,
-	    "sin_family %d", l3addr->sa_family);
-
-	rt = rtalloc1(l3addr, 0);
 	if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) {
 		int s;
 		struct ifaddr *ifa;
@@ -2481,19 +2475,14 @@ in6_lltable_rtcheck(struct ifnet *ifp,
 		ifa = ifaof_ifpforaddr(l3addr, ifp);
 		if (ifa != NULL) {
 			pserialize_read_exit(s);
-			if (rt != NULL)
-				rt_unref(rt);
 			return 0;
 		}
 		pserialize_read_exit(s);
 		log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n",
 		    IN6_PRINT(ip6buf,
 		    &((const struct sockaddr_in6 *)l3addr)->sin6_addr));
-		if (rt != NULL)
-			rt_unref(rt);
 		return EINVAL;
 	}
-	rt_unref(rt);
 	return 0;
 }
 
@@ -2585,7 +2574,7 @@ in6_lltable_delete(struct lltable *llt, 
 
 static struct llentry *
 in6_lltable_create(struct lltable *llt, u_int flags,
-	const struct sockaddr *l3addr)
+    const struct sockaddr *l3addr, const struct rtentry *rt)
 {
 	const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
 	struct ifnet *ifp = llt->llt_ifp;
@@ -2608,7 +2597,7 @@ in6_lltable_create(struct lltable *llt, 
 	 * verify this.
 	 */
 	if (!(flags & LLE_IFADDR) &&
-	    in6_lltable_rtcheck(ifp, flags, l3addr) != 0)
+	    in6_lltable_rtcheck(ifp, flags, l3addr, rt) != 0)
 		return NULL;
 
 	lle = in6_lltable_new(&sin6->sin6_addr, flags);

Index: src/sys/netinet6/nd6.c
diff -u src/sys/netinet6/nd6.c:1.232.2.2 src/sys/netinet6/nd6.c:1.232.2.3
--- src/sys/netinet6/nd6.c:1.232.2.2	Tue Oct 24 09:00:22 2017
+++ src/sys/netinet6/nd6.c	Fri Nov 17 20:24:05 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6.c,v 1.232.2.2 2017/10/24 09:00:22 snj Exp $	*/
+/*	$NetBSD: nd6.c,v 1.232.2.3 2017/11/17 20:24:05 snj Exp $	*/
 /*	$KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.232.2.2 2017/10/24 09:00:22 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.232.2.3 2017/11/17 20:24:05 snj Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -950,14 +950,17 @@ nd6_create(const struct in6_addr *addr6,
 {
 	struct sockaddr_in6 sin6;
 	struct llentry *ln;
+	struct rtentry *rt;
 
 	sockaddr_in6_init(&sin6, addr6, 0, 0, 0);
+	rt = rtalloc1(sin6tosa(&sin6), 0);
 
 	IF_AFDATA_WLOCK(ifp);
-	ln = lla_create(LLTABLE6(ifp), LLE_EXCLUSIVE,
-	    sin6tosa(&sin6));
+	ln = lla_create(LLTABLE6(ifp), LLE_EXCLUSIVE, sin6tosa(&sin6), rt);
 	IF_AFDATA_WUNLOCK(ifp);
 
+	if (rt != NULL)
+		rt_unref(rt);
 	if (ln != NULL)
 		ln->ln_state = ND6_LLINFO_NOSTATE;
 

Reply via email to