Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 20 May 2018 04:38:04 +0000 (UTC)
From:      Matt Macy <mmacy@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r333915 - head/sys/netinet
Message-ID:  <201805200438.w4K4c4BQ073120@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mmacy
Date: Sun May 20 04:38:04 2018
New Revision: 333915
URL: https://svnweb.freebsd.org/changeset/base/333915

Log:
  inpcb: defer destruction of inpcb until after a grace period has elapsed
  
  in_pcbfree will remove the incpb from the list and release the rtentry
  while the vnet is set, but the actual destruction will be deferred
  until any threads in a (not yet used) epoch section, no longer potentially
  have references.

Modified:
  head/sys/netinet/in_pcb.c
  head/sys/netinet/in_pcb.h

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c	Sun May 20 04:32:48 2018	(r333914)
+++ head/sys/netinet/in_pcb.c	Sun May 20 04:38:04 2018	(r333915)
@@ -1336,44 +1336,22 @@ in_pcblist_rele_rlocked(epoch_context_t ctx)
 	free(il, M_TEMP);
 }
 
-/*
- * Unconditionally schedule an inpcb to be freed by decrementing its
- * reference count, which should occur only after the inpcb has been detached
- * from its socket.  If another thread holds a temporary reference (acquired
- * using in_pcbref()) then the free is deferred until that reference is
- * released using in_pcbrele(), but the inpcb is still unlocked.  Almost all
- * work, including removal from global lists, is done in this context, where
- * the pcbinfo lock is held.
- */
-void
-in_pcbfree(struct inpcb *inp)
+static void
+in_pcbfree_deferred(epoch_context_t ctx)
 {
-	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
-
+	struct inpcb *inp;
+	struct inpcbinfo *pcbinfo;
 #ifdef INET6
 	struct ip6_moptions *im6o = NULL;
 #endif
 #ifdef INET
 	struct ip_moptions *imo = NULL;
 #endif
-	KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__));
 
-	KASSERT((inp->inp_flags2 & INP_FREED) == 0,
-	    ("%s: called twice for pcb %p", __func__, inp));
-	if (inp->inp_flags2 & INP_FREED) {
-		INP_WUNLOCK(inp);
-		return;
-	}
+	inp = __containerof(ctx, struct inpcb, inp_epoch_ctx);
+	pcbinfo = inp->inp_pcbinfo;
 
-#ifdef INVARIANTS
-	if (pcbinfo == &V_tcbinfo) {
-		INP_INFO_LOCK_ASSERT(pcbinfo);
-	} else {
-		INP_INFO_WLOCK_ASSERT(pcbinfo);
-	}
-#endif
-	INP_WLOCK_ASSERT(inp);
-
+	INP_WLOCK(inp);	
 #ifdef INET
 	imo = inp->inp_moptions;
 	inp->inp_moptions = NULL;
@@ -1383,10 +1361,6 @@ in_pcbfree(struct inpcb *inp)
 	if (inp->inp_sp != NULL)
 		ipsec_delete_pcbpolicy(inp);
 #endif
-	INP_LIST_WLOCK(pcbinfo);
-	inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
-	in_pcbremlists(inp);
-	INP_LIST_WUNLOCK(pcbinfo);
 #ifdef INET6
 	if (inp->inp_vflag & INP_IPV6PROTO) {
 		ip6_freepcbopts(inp->in6p_outputopts);
@@ -1396,7 +1370,6 @@ in_pcbfree(struct inpcb *inp)
 #endif
 	if (inp->inp_options)
 		(void)m_free(inp->inp_options);
-	RO_INVALIDATE_CACHE(&inp->inp_route);
 
 	inp->inp_vflag = 0;
 	inp->inp_flags2 |= INP_FREED;
@@ -1404,14 +1377,54 @@ in_pcbfree(struct inpcb *inp)
 #ifdef MAC
 	mac_inpcb_destroy(inp);
 #endif
+	if (!in_pcbrele_wlocked(inp))
+		INP_WUNLOCK(inp);
 #ifdef INET6
 	ip6_freemoptions(im6o);
 #endif
 #ifdef INET
 	inp_freemoptions(imo);
 #endif
-	if (!in_pcbrele_wlocked(inp))
+}
+
+/*
+ * Unconditionally schedule an inpcb to be freed by decrementing its
+ * reference count, which should occur only after the inpcb has been detached
+ * from its socket.  If another thread holds a temporary reference (acquired
+ * using in_pcbref()) then the free is deferred until that reference is
+ * released using in_pcbrele(), but the inpcb is still unlocked.  Almost all
+ * work, including removal from global lists, is done in this context, where
+ * the pcbinfo lock is held.
+ */
+void
+in_pcbfree(struct inpcb *inp)
+{
+	struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+
+	KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__));
+	KASSERT((inp->inp_flags2 & INP_FREED) == 0,
+	    ("%s: called twice for pcb %p", __func__, inp));
+	if (inp->inp_flags2 & INP_FREED) {
 		INP_WUNLOCK(inp);
+		return;
+	}
+
+#ifdef INVARIANTS
+	if (pcbinfo == &V_tcbinfo) {
+		INP_INFO_LOCK_ASSERT(pcbinfo);
+	} else {
+		INP_INFO_WLOCK_ASSERT(pcbinfo);
+	}
+#endif
+	INP_WLOCK_ASSERT(inp);
+	/* Remove first from list */
+	INP_LIST_WLOCK(pcbinfo);
+	inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
+	in_pcbremlists(inp);
+	INP_LIST_WUNLOCK(pcbinfo);
+	RO_INVALIDATE_CACHE(&inp->inp_route);
+	INP_WUNLOCK(inp);
+	epoch_call(net_epoch_preempt, &inp->inp_epoch_ctx, in_pcbfree_deferred);
 }
 
 /*

Modified: head/sys/netinet/in_pcb.h
==============================================================================
--- head/sys/netinet/in_pcb.h	Sun May 20 04:32:48 2018	(r333914)
+++ head/sys/netinet/in_pcb.h	Sun May 20 04:38:04 2018	(r333915)
@@ -328,6 +328,7 @@ struct inpcb {
 	LIST_ENTRY(inpcb) inp_list;	/* (p/l) list for all PCBs for proto */
 	                                /* (p[w]) for list iteration */
 	                                /* (p[r]/l) for addition/removal */
+	struct epoch_context inp_epoch_ctx;
 };
 #endif	/* _KERNEL */
 



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