Defer inpcb deletion until after a grace period has elapsed

Deferring the actual free of the inpcb until after a grace
period has elapsed will allow us to convert the inpcbinfo
info and hash read locks to epoch.

Reviewed by: gallatin, jtl
Sponsored by: Limelight Networks
Differential Revision: https://reviews.freebsd.org/D15510
This commit is contained in:
Matt Macy 2018-06-12 22:18:15 +00:00
parent 25f0326aea
commit addf2b2009
2 changed files with 43 additions and 41 deletions

View File

@ -1569,6 +1569,45 @@ in_pcblist_rele_rlocked(epoch_context_t ctx)
free(il, M_TEMP);
}
static void
in_pcbfree_deferred(epoch_context_t ctx)
{
struct inpcb *inp;
struct inpcbinfo *pcbinfo;
int released __unused;
inp = __containerof(ctx, struct inpcb, inp_epoch_ctx);
pcbinfo = inp->inp_pcbinfo;
INP_WLOCK(inp);
#ifdef INET
inp_freemoptions(inp->inp_moptions);
inp->inp_moptions = NULL;
#endif
/* XXXRW: Do as much as possible here. */
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (inp->inp_sp != NULL)
ipsec_delete_pcbpolicy(inp);
#endif
#ifdef INET6
if (inp->inp_vflag & INP_IPV6PROTO) {
ip6_freepcbopts(inp->in6p_outputopts);
ip6_freemoptions(inp->in6p_moptions);
inp->in6p_moptions = NULL;
}
#endif
if (inp->inp_options)
(void)m_free(inp->inp_options);
inp->inp_vflag = 0;
crfree(inp->inp_cred);
#ifdef MAC
mac_inpcb_destroy(inp);
#endif
released = in_pcbrele_wlocked(inp);
MPASS(released);
}
/*
* Unconditionally schedule an inpcb to be freed by decrementing its
* reference count, which should occur only after the inpcb has been detached
@ -1583,14 +1622,7 @@ in_pcbfree(struct inpcb *inp)
{
struct inpcbinfo *pcbinfo = inp->inp_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) {
@ -1606,45 +1638,14 @@ in_pcbfree(struct inpcb *inp)
}
#endif
INP_WLOCK_ASSERT(inp);
#ifdef INET
imo = inp->inp_moptions;
inp->inp_moptions = NULL;
#endif
/* XXXRW: Do as much as possible here. */
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
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);
im6o = inp->in6p_moptions;
inp->in6p_moptions = NULL;
}
#endif
if (inp->inp_options)
(void)m_free(inp->inp_options);
RO_INVALIDATE_CACHE(&inp->inp_route);
inp->inp_vflag = 0;
/* mark as destruction in progress */
inp->inp_flags2 |= INP_FREED;
crfree(inp->inp_cred);
#ifdef MAC
mac_inpcb_destroy(inp);
#endif
#ifdef INET6
ip6_freemoptions(im6o);
#endif
#ifdef INET
inp_freemoptions(imo);
#endif
if (!in_pcbrele_wlocked(inp))
INP_WUNLOCK(inp);
INP_WUNLOCK(inp);
epoch_call(net_epoch_preempt, &inp->inp_epoch_ctx, in_pcbfree_deferred);
}
/*

View File

@ -325,6 +325,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 */