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>