svn commit: r333915 - head/sys/netinet
Matt Macy
mmacy at FreeBSD.org
Sun May 20 04:38:05 UTC 2018
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 */
More information about the svn-src-all
mailing list