summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorozaki-r <ozaki-r@NetBSD.org>2015-10-20 07:46:59 +0000
committerozaki-r <ozaki-r@NetBSD.org>2015-10-20 07:46:59 +0000
commit0e6f84df0cd57cf8a862c5d67a1bd740c574348a (patch)
tree1a1d5dab0b8719f284defb412d45f6a680e562f0 /sys
parent4fef7ea9d802194645618d3abe6667494f3a9f6c (diff)
Stop callout in arp_rtrequest(RTM_DELETE)
This change fixes arptimer panic after removing an interface (say by drvctl -d), which is reported by Takahiro Hayashi. This change also fixes llentry's reference counting; we have to take into account rtentry#rt_llinfo as well as arptimer.
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/if_arp.c30
1 files changed, 19 insertions, 11 deletions
diff --git a/sys/netinet/if_arp.c b/sys/netinet/if_arp.c
index 86cc308e294..d2c9d3ccb0b 100644
--- a/sys/netinet/if_arp.c
+++ b/sys/netinet/if_arp.c
@@ -1,4 +1,4 @@
-/* $NetBSD: if_arp.c,v 1.190 2015/10/20 07:35:15 ozaki-r Exp $ */
+/* $NetBSD: if_arp.c,v 1.191 2015/10/20 07:46:59 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.190 2015/10/20 07:35:15 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.191 2015/10/20 07:46:59 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@@ -635,6 +635,7 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info)
break;
}
rt->rt_llinfo = la;
+ LLE_ADDREF(la);
switch (ifp->if_type) {
#if NTOKEN > 0
case IFT_ISO88025:
@@ -662,17 +663,14 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info)
rt->rt_llinfo = NULL;
rt->rt_flags &= ~RTF_LLINFO;
+ /* Have to do before IF_AFDATA_WLOCK to avoid deadlock */
+ callout_halt(&la->la_timer, &la->lle_lock);
+ /* XXX: LOR avoidance. We still have ref on lle. */
LLE_RUNLOCK(la);
flags |= LLE_EXCLUSIVE;
IF_AFDATA_WLOCK(ifp);
-
- la = lla_lookup(LLTABLE(ifp), flags, rt_getkey(rt));
- /* This shouldn't happen */
- if (la == NULL) {
- IF_AFDATA_WUNLOCK(ifp);
- break;
- }
+ LLE_WLOCK(la);
if (la->la_opaque != NULL) {
switch (ifp->if_type) {
@@ -695,10 +693,20 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info)
la->la_rt->rt_refcnt--;
la->la_rt = NULL;
}
- llentry_free(la);
- IF_AFDATA_WUNLOCK(ifp);
+ /* Guard against race with other llentry_free(). */
+ if (la->la_flags & LLE_LINKED) {
+ size_t pkts_dropped;
+
+ LLE_REMREF(la);
+ pkts_dropped = llentry_free(la);
+ ARP_STATADD(ARP_STAT_DFRDROPPED, pkts_dropped);
+ } else {
+ LLE_FREE_LOCKED(la);
+ }
la = NULL;
+
+ IF_AFDATA_WUNLOCK(ifp);
}
if (la != NULL) {