Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys Fix a deadlock between a route update and lltable



details:   https://anonhg.NetBSD.org/src/rev/0ada6d402f46
branches:  trunk
changeset: 827726:0ada6d402f46
user:      ozaki-r <ozaki-r%NetBSD.org@localhost>
date:      Fri Nov 10 07:24:28 2017 +0000

description:
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.

diffstat:

 sys/net/if_llatbl.c  |  21 ++++++++++++++++-----
 sys/net/if_llatbl.h  |   9 +++++----
 sys/netinet/if_arp.c |  18 ++++++++++++++----
 sys/netinet/in.c     |  15 +++++++--------
 sys/netinet6/in6.c   |  20 ++++++--------------
 sys/netinet6/nd6.c   |  11 +++++++----
 6 files changed, 55 insertions(+), 39 deletions(-)

diffs (truncated from 326 to 300 lines):

diff -r 59b17bd7ea15 -r 0ada6d402f46 sys/net/if_llatbl.c
--- a/sys/net/if_llatbl.c       Fri Nov 10 07:15:32 2017 +0000
+++ b/sys/net/if_llatbl.c       Fri Nov 10 07:24:28 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_llatbl.c,v 1.21 2017/06/28 04:14:53 ozaki-r Exp $   */
+/*     $NetBSD: if_llatbl.c,v 1.22 2017/11/10 07:24:28 ozaki-r Exp $   */
 /*
  * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved.
  * Copyright (c) 2004-2008 Qing Li. All rights reserved.
@@ -386,7 +386,7 @@
            (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 @@
        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 @@
                    (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 @@
                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 @@
 #else
                (void)laflags;
 #endif
-
                break;
+           }
 
        case RTM_DELETE:
                IF_AFDATA_WLOCK(ifp);
diff -r 59b17bd7ea15 -r 0ada6d402f46 sys/net/if_llatbl.h
--- a/sys/net/if_llatbl.h       Fri Nov 10 07:15:32 2017 +0000
+++ b/sys/net/if_llatbl.h       Fri Nov 10 07:24:28 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_llatbl.h,v 1.12 2017/06/23 05:46:10 ozaki-r Exp $   */
+/*     $NetBSD: if_llatbl.h,v 1.13 2017/11/10 07:24:28 ozaki-r Exp $   */
 /*
  * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved.
  * Copyright (c) 2004-2008 Qing Li. All rights reserved.
@@ -190,7 +190,7 @@
 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 @@
 }
 
 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
diff -r 59b17bd7ea15 -r 0ada6d402f46 sys/netinet/if_arp.c
--- a/sys/netinet/if_arp.c      Fri Nov 10 07:15:32 2017 +0000
+++ b/sys/netinet/if_arp.c      Fri Nov 10 07:24:28 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: if_arp.c,v 1.253 2017/06/27 12:21:54 roy Exp $ */
+/*     $NetBSD: if_arp.c,v 1.254 2017/11/10 07:24:28 ozaki-r 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.253 2017/06/27 12:21:54 roy Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.254 2017/11/10 07:24:28 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -754,10 +754,15 @@
        }
 #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 @@
        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);
diff -r 59b17bd7ea15 -r 0ada6d402f46 sys/netinet/in.c
--- a/sys/netinet/in.c  Fri Nov 10 07:15:32 2017 +0000
+++ b/sys/netinet/in.c  Fri Nov 10 07:24:28 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: in.c,v 1.208 2017/11/10 07:15:32 ozaki-r Exp $ */
+/*     $NetBSD: in.c,v 1.209 2017/11/10 07:24:28 ozaki-r 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.208 2017/11/10 07:15:32 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.209 2017/11/10 07:24:28 ozaki-r Exp $");
 
 #include "arp.h"
 
@@ -1975,12 +1975,11 @@
 }
 
 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;
 
-       rt = rtalloc1(l3addr, 0);
        if (rt == NULL)
                return error;
 
@@ -2041,7 +2040,6 @@
 
        error = 0;
 error:
-       rt_unref(rt);
        return error;
 }
 
@@ -2132,7 +2130,8 @@
 }
 
 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;
@@ -2157,7 +2156,7 @@
         * 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);
diff -r 59b17bd7ea15 -r 0ada6d402f46 sys/netinet6/in6.c
--- a/sys/netinet6/in6.c        Fri Nov 10 07:15:32 2017 +0000
+++ b/sys/netinet6/in6.c        Fri Nov 10 07:24:28 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: in6.c,v 1.249 2017/11/10 07:15:32 ozaki-r Exp $        */
+/*     $NetBSD: in6.c,v 1.250 2017/11/10 07:24:28 ozaki-r 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.249 2017/11/10 07:15:32 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.250 2017/11/10 07:24:28 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -2458,14 +2458,11 @@
 }
 
 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];
 
-       rt = rtalloc1(l3addr, 0);
        if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) {
                int s;
                struct ifaddr *ifa;
@@ -2478,19 +2475,14 @@
                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;
 }
 
@@ -2582,7 +2574,7 @@
 
 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;
@@ -2605,7 +2597,7 @@
         * 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);
diff -r 59b17bd7ea15 -r 0ada6d402f46 sys/netinet6/nd6.c
--- a/sys/netinet6/nd6.c        Fri Nov 10 07:15:32 2017 +0000
+++ b/sys/netinet6/nd6.c        Fri Nov 10 07:24:28 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: nd6.c,v 1.236 2017/10/05 03:42:14 ozaki-r Exp $        */
+/*     $NetBSD: nd6.c,v 1.237 2017/11/10 07:24:28 ozaki-r Exp $        */
 /*     $KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $   */
 
 /*
@@ -31,7 +31,7 @@
  */
 



Home | Main Index | Thread Index | Old Index