aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet
diff options
context:
space:
mode:
authorRandall Stewart <rrs@FreeBSD.org>2015-02-09 19:28:11 +0000
committerRandall Stewart <rrs@FreeBSD.org>2015-02-09 19:28:11 +0000
commit2575fbb82713dcfca8a5a8794119a7cd4c713405 (patch)
tree9e93f59504cb4e84deed147e8803f9f6900e81e9 /sys/netinet
parent0174acd43917f49a027a8d47811f8f91b99dc866 (diff)
downloadsrc-2575fbb82713dcfca8a5a8794119a7cd4c713405.tar.gz
src-2575fbb82713dcfca8a5a8794119a7cd4c713405.zip
This fixes a bug in the way that the LLE timers for nd6
and arp were being used. They basically would pass in the mutex to the callout_init. Because they used this method to the callout system, it was possible to "stop" the callout. When flushing the table and you stopped the running callout, the callout_stop code would return 1 indicating that it was going to stop the callout (that was about to run on the callout_wheel blocked by the function calling the stop). Now when 1 was returned, it would lower the reference count one extra time for the stopped timer, then a few lines later delete the memory. Of course the callout_wheel was stuck in the lock code and would then crash since it was accessing freed memory. By using callout_init(c, 1) we always get a 0 back and the reference counting bug does not rear its head. We do have to make a few adjustments to the callouts themselves though to make sure it does the proper thing if rescheduled as well as gets the lock. Commented upon by hiren and sbruno See Phabricator D1777 for more details. Commented upon by hiren and sbruno Reviewed by: adrian, jhb and bz Sponsored by: Netflix Inc.
Notes
Notes: svn path=/head/; revision=278472
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/if_ether.c22
-rw-r--r--sys/netinet/in.c3
2 files changed, 21 insertions, 4 deletions
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
index 5011fc418d83..78ec2f40f781 100644
--- a/sys/netinet/if_ether.c
+++ b/sys/netinet/if_ether.c
@@ -166,10 +166,28 @@ arptimer(void *arg)
struct ifnet *ifp;
if (lle->la_flags & LLE_STATIC) {
- LLE_WUNLOCK(lle);
return;
}
-
+ LLE_WLOCK(lle);
+ if (callout_pending(&lle->la_timer)) {
+ /*
+ * Here we are a bit odd here in the treatment of
+ * active/pending. If the pending bit is set, it got
+ * rescheduled before I ran. The active
+ * bit we ignore, since if it was stopped
+ * in ll_tablefree() and was currently running
+ * it would have return 0 so the code would
+ * not have deleted it since the callout could
+ * not be stopped so we want to go through
+ * with the delete here now. If the callout
+ * was restarted, the pending bit will be back on and
+ * we just want to bail since the callout_reset would
+ * return 1 and our reference would have been removed
+ * by arpresolve() below.
+ */
+ LLE_WUNLOCK(lle);
+ return;
+ }
ifp = lle->lle_tbl->llt_ifp;
CURVNET_SET(ifp->if_vnet);
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index 42cf7e607e7b..bfcb33a641ad 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -962,8 +962,7 @@ in_lltable_new(const struct sockaddr *l3addr, u_int flags)
lle->base.lle_refcnt = 1;
lle->base.lle_free = in_lltable_free;
LLE_LOCK_INIT(&lle->base);
- callout_init_rw(&lle->base.la_timer, &lle->base.lle_lock,
- CALLOUT_RETURNUNLOCKED);
+ callout_init(&lle->base.la_timer, 1);
return (&lle->base);
}