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.
This commit is contained in:
parent
faf7d24046
commit
1c27f64d6f
@ -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 Luigi Rizzo, Alessandro Cerri. All rights reserved.
|
||||||
* Copyright (c) 2004-2008 Qing Li. All rights reserved.
|
* Copyright (c) 2004-2008 Qing Li. All rights reserved.
|
||||||
@ -386,7 +386,7 @@ llentry_alloc(struct ifnet *ifp, struct lltable *lt,
|
|||||||
(ifp->if_flags & IFF_NOARP) == 0) {
|
(ifp->if_flags & IFF_NOARP) == 0) {
|
||||||
#endif
|
#endif
|
||||||
IF_AFDATA_WLOCK(ifp);
|
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);
|
IF_AFDATA_WUNLOCK(ifp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,7 +656,12 @@ lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expir
|
|||||||
error = 0;
|
error = 0;
|
||||||
|
|
||||||
switch (rtm_type) {
|
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 */
|
/* Add static LLE */
|
||||||
IF_AFDATA_WLOCK(ifp);
|
IF_AFDATA_WLOCK(ifp);
|
||||||
lle = lla_lookup(llt, 0, dst);
|
lle = lla_lookup(llt, 0, dst);
|
||||||
@ -666,15 +671,19 @@ lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expir
|
|||||||
(lle->la_flags & LLE_STATIC || lle->la_expire == 0)) {
|
(lle->la_flags & LLE_STATIC || lle->la_expire == 0)) {
|
||||||
LLE_RUNLOCK(lle);
|
LLE_RUNLOCK(lle);
|
||||||
IF_AFDATA_WUNLOCK(ifp);
|
IF_AFDATA_WUNLOCK(ifp);
|
||||||
|
if (rt != NULL)
|
||||||
|
rt_unref(rt);
|
||||||
error = EEXIST;
|
error = EEXIST;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (lle != NULL)
|
if (lle != NULL)
|
||||||
LLE_RUNLOCK(lle);
|
LLE_RUNLOCK(lle);
|
||||||
|
|
||||||
lle = lla_create(llt, 0, dst);
|
lle = lla_create(llt, 0, dst, rt);
|
||||||
if (lle == NULL) {
|
if (lle == NULL) {
|
||||||
IF_AFDATA_WUNLOCK(ifp);
|
IF_AFDATA_WUNLOCK(ifp);
|
||||||
|
if (rt != NULL)
|
||||||
|
rt_unref(rt);
|
||||||
error = ENOMEM;
|
error = ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -703,6 +712,8 @@ lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expir
|
|||||||
laflags = lle->la_flags;
|
laflags = lle->la_flags;
|
||||||
LLE_WUNLOCK(lle);
|
LLE_WUNLOCK(lle);
|
||||||
IF_AFDATA_WUNLOCK(ifp);
|
IF_AFDATA_WUNLOCK(ifp);
|
||||||
|
if (rt != NULL)
|
||||||
|
rt_unref(rt);
|
||||||
#if defined(INET) && NARP > 0
|
#if defined(INET) && NARP > 0
|
||||||
/* gratuitous ARP */
|
/* gratuitous ARP */
|
||||||
if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) {
|
if ((laflags & LLE_PUB) && dst->sa_family == AF_INET) {
|
||||||
@ -721,8 +732,8 @@ lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expir
|
|||||||
#else
|
#else
|
||||||
(void)laflags;
|
(void)laflags;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case RTM_DELETE:
|
case RTM_DELETE:
|
||||||
IF_AFDATA_WLOCK(ifp);
|
IF_AFDATA_WLOCK(ifp);
|
||||||
|
@ -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 Luigi Rizzo, Alessandro Cerri. All rights reserved.
|
||||||
* Copyright (c) 2004-2008 Qing Li. 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,
|
typedef struct llentry *(llt_lookup_t)(struct lltable *, u_int flags,
|
||||||
const struct sockaddr *l3addr);
|
const struct sockaddr *l3addr);
|
||||||
typedef struct llentry *(llt_create_t)(struct lltable *, u_int flags,
|
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,
|
typedef int (llt_delete_t)(struct lltable *, u_int flags,
|
||||||
const struct sockaddr *l3addr);
|
const struct sockaddr *l3addr);
|
||||||
typedef void (llt_prefix_free_t)(struct lltable *,
|
typedef void (llt_prefix_free_t)(struct lltable *,
|
||||||
@ -297,10 +297,11 @@ lla_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static __inline struct llentry *
|
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
|
static __inline int
|
||||||
|
@ -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.
|
* Copyright (c) 1998, 2000, 2008 The NetBSD Foundation, Inc.
|
||||||
@ -68,7 +68,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#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
|
#ifdef _KERNEL_OPT
|
||||||
#include "opt_ddb.h"
|
#include "opt_ddb.h"
|
||||||
@ -754,10 +754,15 @@ notfound:
|
|||||||
}
|
}
|
||||||
#undef _IFF_NOARP
|
#undef _IFF_NOARP
|
||||||
if (la == NULL) {
|
if (la == NULL) {
|
||||||
|
struct rtentry *_rt;
|
||||||
|
|
||||||
create_lookup = "create";
|
create_lookup = "create";
|
||||||
|
_rt = rtalloc1(dst, 0);
|
||||||
IF_AFDATA_WLOCK(ifp);
|
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_AFDATA_WUNLOCK(ifp);
|
||||||
|
if (_rt != NULL)
|
||||||
|
rt_unref(_rt);
|
||||||
if (la == NULL)
|
if (la == NULL)
|
||||||
ARP_STATINC(ARP_STAT_ALLOCFAIL);
|
ARP_STATINC(ARP_STAT_ALLOCFAIL);
|
||||||
else {
|
else {
|
||||||
@ -1452,9 +1457,14 @@ arpcreate(struct ifnet *ifp, struct mbuf *m, const struct in_addr *addr,
|
|||||||
la = arplookup(ifp, m, addr, sa, wlock);
|
la = arplookup(ifp, m, addr, sa, wlock);
|
||||||
|
|
||||||
if (la == NULL) {
|
if (la == NULL) {
|
||||||
|
struct rtentry *rt;
|
||||||
|
|
||||||
|
rt = rtalloc1(sa, 0);
|
||||||
IF_AFDATA_WLOCK(ifp);
|
IF_AFDATA_WLOCK(ifp);
|
||||||
la = lla_create(LLTABLE(ifp), flags, sa);
|
la = lla_create(LLTABLE(ifp), flags, sa, rt);
|
||||||
IF_AFDATA_WUNLOCK(ifp);
|
IF_AFDATA_WUNLOCK(ifp);
|
||||||
|
if (rt != NULL)
|
||||||
|
rt_unref(rt);
|
||||||
|
|
||||||
if (la != NULL)
|
if (la != NULL)
|
||||||
arp_init_llentry(ifp, la);
|
arp_init_llentry(ifp, la);
|
||||||
|
@ -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.
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
||||||
@ -91,7 +91,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#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"
|
#include "arp.h"
|
||||||
|
|
||||||
@ -1975,12 +1975,11 @@ in_lltable_free_entry(struct lltable *llt, struct llentry *lle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
int error = EINVAL;
|
||||||
|
|
||||||
rt = rtalloc1(l3addr, 0);
|
|
||||||
if (rt == NULL)
|
if (rt == NULL)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@ -2041,7 +2040,6 @@ in_lltable_rtcheck(struct ifnet *ifp, u_int flags, const struct sockaddr *l3addr
|
|||||||
|
|
||||||
error = 0;
|
error = 0;
|
||||||
error:
|
error:
|
||||||
rt_unref(rt);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2132,7 +2130,8 @@ in_lltable_delete(struct lltable *llt, u_int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct llentry *
|
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;
|
const struct sockaddr_in *sin = (const struct sockaddr_in *)l3addr;
|
||||||
struct ifnet *ifp = llt->llt_ifp;
|
struct ifnet *ifp = llt->llt_ifp;
|
||||||
@ -2157,7 +2156,7 @@ in_lltable_create(struct lltable *llt, u_int flags, const struct sockaddr *l3add
|
|||||||
* verify this.
|
* verify this.
|
||||||
*/
|
*/
|
||||||
if (!(flags & LLE_IFADDR) &&
|
if (!(flags & LLE_IFADDR) &&
|
||||||
in_lltable_rtcheck(ifp, flags, l3addr) != 0)
|
in_lltable_rtcheck(ifp, flags, l3addr, rt) != 0)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
lle = in_lltable_new(sin->sin_addr, flags);
|
lle = in_lltable_new(sin->sin_addr, flags);
|
||||||
|
@ -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 $ */
|
/* $KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -62,7 +62,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#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
|
#ifdef _KERNEL_OPT
|
||||||
#include "opt_inet.h"
|
#include "opt_inet.h"
|
||||||
@ -2458,14 +2458,11 @@ in6_lltable_free_entry(struct lltable *llt, struct llentry *lle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
in6_lltable_rtcheck(struct ifnet *ifp,
|
in6_lltable_rtcheck(struct ifnet *ifp, u_int flags,
|
||||||
u_int flags,
|
const struct sockaddr *l3addr, const struct rtentry *rt)
|
||||||
const struct sockaddr *l3addr)
|
|
||||||
{
|
{
|
||||||
struct rtentry *rt;
|
|
||||||
char ip6buf[INET6_ADDRSTRLEN];
|
char ip6buf[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
rt = rtalloc1(l3addr, 0);
|
|
||||||
if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) {
|
if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) {
|
||||||
int s;
|
int s;
|
||||||
struct ifaddr *ifa;
|
struct ifaddr *ifa;
|
||||||
@ -2478,19 +2475,14 @@ in6_lltable_rtcheck(struct ifnet *ifp,
|
|||||||
ifa = ifaof_ifpforaddr(l3addr, ifp);
|
ifa = ifaof_ifpforaddr(l3addr, ifp);
|
||||||
if (ifa != NULL) {
|
if (ifa != NULL) {
|
||||||
pserialize_read_exit(s);
|
pserialize_read_exit(s);
|
||||||
if (rt != NULL)
|
|
||||||
rt_unref(rt);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
pserialize_read_exit(s);
|
pserialize_read_exit(s);
|
||||||
log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n",
|
log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n",
|
||||||
IN6_PRINT(ip6buf,
|
IN6_PRINT(ip6buf,
|
||||||
&((const struct sockaddr_in6 *)l3addr)->sin6_addr));
|
&((const struct sockaddr_in6 *)l3addr)->sin6_addr));
|
||||||
if (rt != NULL)
|
|
||||||
rt_unref(rt);
|
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
rt_unref(rt);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2582,7 +2574,7 @@ in6_lltable_delete(struct lltable *llt, u_int flags,
|
|||||||
|
|
||||||
static struct llentry *
|
static struct llentry *
|
||||||
in6_lltable_create(struct lltable *llt, u_int flags,
|
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;
|
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr;
|
||||||
struct ifnet *ifp = llt->llt_ifp;
|
struct ifnet *ifp = llt->llt_ifp;
|
||||||
@ -2605,7 +2597,7 @@ in6_lltable_create(struct lltable *llt, u_int flags,
|
|||||||
* verify this.
|
* verify this.
|
||||||
*/
|
*/
|
||||||
if (!(flags & LLE_IFADDR) &&
|
if (!(flags & LLE_IFADDR) &&
|
||||||
in6_lltable_rtcheck(ifp, flags, l3addr) != 0)
|
in6_lltable_rtcheck(ifp, flags, l3addr, rt) != 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
lle = in6_lltable_new(&sin6->sin6_addr, flags);
|
lle = in6_lltable_new(&sin6->sin6_addr, flags);
|
||||||
|
@ -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 $ */
|
/* $KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -31,7 +31,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.236 2017/10/05 03:42:14 ozaki-r Exp $");
|
__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.237 2017/11/10 07:24:28 ozaki-r Exp $");
|
||||||
|
|
||||||
#ifdef _KERNEL_OPT
|
#ifdef _KERNEL_OPT
|
||||||
#include "opt_net_mpsafe.h"
|
#include "opt_net_mpsafe.h"
|
||||||
@ -950,14 +950,17 @@ nd6_create(const struct in6_addr *addr6, const struct ifnet *ifp)
|
|||||||
{
|
{
|
||||||
struct sockaddr_in6 sin6;
|
struct sockaddr_in6 sin6;
|
||||||
struct llentry *ln;
|
struct llentry *ln;
|
||||||
|
struct rtentry *rt;
|
||||||
|
|
||||||
sockaddr_in6_init(&sin6, addr6, 0, 0, 0);
|
sockaddr_in6_init(&sin6, addr6, 0, 0, 0);
|
||||||
|
rt = rtalloc1(sin6tosa(&sin6), 0);
|
||||||
|
|
||||||
IF_AFDATA_WLOCK(ifp);
|
IF_AFDATA_WLOCK(ifp);
|
||||||
ln = lla_create(LLTABLE6(ifp), LLE_EXCLUSIVE,
|
ln = lla_create(LLTABLE6(ifp), LLE_EXCLUSIVE, sin6tosa(&sin6), rt);
|
||||||
sin6tosa(&sin6));
|
|
||||||
IF_AFDATA_WUNLOCK(ifp);
|
IF_AFDATA_WUNLOCK(ifp);
|
||||||
|
|
||||||
|
if (rt != NULL)
|
||||||
|
rt_unref(rt);
|
||||||
if (ln != NULL)
|
if (ln != NULL)
|
||||||
ln->ln_state = ND6_LLINFO_NOSTATE;
|
ln->ln_state = ND6_LLINFO_NOSTATE;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user