Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 15 Feb 2015 13:57:45 +0000 (UTC)
From:      Randall Stewart <rrs@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r278801 - in stable/10/sys: netinet netinet6
Message-ID:  <201502151357.t1FDvjdu051912@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rrs
Date: Sun Feb 15 13:57:44 2015
New Revision: 278801
URL: https://svnweb.freebsd.org/changeset/base/278801

Log:
  MFC of r278472
  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.
  
  Sponsored by:	Netflix Inc.

Modified:
  stable/10/sys/netinet/if_ether.c
  stable/10/sys/netinet/in.c
  stable/10/sys/netinet6/in6.c
  stable/10/sys/netinet6/nd6.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/netinet/if_ether.c
==============================================================================
--- stable/10/sys/netinet/if_ether.c	Sun Feb 15 13:24:32 2015	(r278800)
+++ stable/10/sys/netinet/if_ether.c	Sun Feb 15 13:57:44 2015	(r278801)
@@ -170,10 +170,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);
 

Modified: stable/10/sys/netinet/in.c
==============================================================================
--- stable/10/sys/netinet/in.c	Sun Feb 15 13:24:32 2015	(r278800)
+++ stable/10/sys/netinet/in.c	Sun Feb 15 13:57:44 2015	(r278801)
@@ -1263,8 +1263,7 @@ in_lltable_new(const struct sockaddr *l3
 	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);
 }

Modified: stable/10/sys/netinet6/in6.c
==============================================================================
--- stable/10/sys/netinet6/in6.c	Sun Feb 15 13:24:32 2015	(r278800)
+++ stable/10/sys/netinet6/in6.c	Sun Feb 15 13:57:44 2015	(r278801)
@@ -2519,8 +2519,7 @@ in6_lltable_new(const struct sockaddr *l
 	lle->base.lle_refcnt = 1;
 	lle->base.lle_free = in6_lltable_free;
 	LLE_LOCK_INIT(&lle->base);
-	callout_init_rw(&lle->base.ln_timer_ch, &lle->base.lle_lock,
-	    CALLOUT_RETURNUNLOCKED);
+	callout_init(&lle->base.ln_timer_ch, 1);
 
 	return (&lle->base);
 }

Modified: stable/10/sys/netinet6/nd6.c
==============================================================================
--- stable/10/sys/netinet6/nd6.c	Sun Feb 15 13:24:32 2015	(r278800)
+++ stable/10/sys/netinet6/nd6.c	Sun Feb 15 13:57:44 2015	(r278801)
@@ -466,9 +466,28 @@ nd6_llinfo_timer(void *arg)
 
 	KASSERT(arg != NULL, ("%s: arg NULL", __func__));
 	ln = (struct llentry *)arg;
-	LLE_WLOCK_ASSERT(ln);
+	LLE_WLOCK(ln);
+	if (callout_pending(&ln->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 nd6_llinfo_settimer_locked above since canceled
+		 * would have been 1.
+		 */
+		LLE_WUNLOCK(ln);
+		return;
+	}
 	ifp = ln->lle_tbl->llt_ifp;
-
 	CURVNET_SET(ifp->if_vnet);
 
 	if (ln->ln_ntick > 0) {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201502151357.t1FDvjdu051912>