Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/in_pcb.c
Show First 20 Lines • Show All 221 Lines • ▼ Show 20 Lines | in_pcbinfo_init(struct inpcbinfo *pcbinfo, const char *name, | ||||
struct inpcbhead *listhead, int hash_nelements, int porthash_nelements, | struct inpcbhead *listhead, int hash_nelements, int porthash_nelements, | ||||
char *inpcbzone_name, uma_init inpcbzone_init, uma_fini inpcbzone_fini, | char *inpcbzone_name, uma_init inpcbzone_init, uma_fini inpcbzone_fini, | ||||
uint32_t inpcbzone_flags, u_int hashfields) | uint32_t inpcbzone_flags, u_int hashfields) | ||||
{ | { | ||||
INP_INFO_LOCK_INIT(pcbinfo, name); | INP_INFO_LOCK_INIT(pcbinfo, name); | ||||
INP_HASH_LOCK_INIT(pcbinfo, "pcbinfohash"); /* XXXRW: argument? */ | INP_HASH_LOCK_INIT(pcbinfo, "pcbinfohash"); /* XXXRW: argument? */ | ||||
INP_LIST_LOCK_INIT(pcbinfo, "pcbinfolist"); | INP_LIST_LOCK_INIT(pcbinfo, "pcbinfolist"); | ||||
pcbinfo->ipi_epoch = ebr_epoch_alloc(5); | |||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
pcbinfo->ipi_vnet = curvnet; | pcbinfo->ipi_vnet = curvnet; | ||||
#endif | #endif | ||||
pcbinfo->ipi_listhead = listhead; | pcbinfo->ipi_listhead = listhead; | ||||
LIST_INIT(pcbinfo->ipi_listhead); | LIST_INIT(pcbinfo->ipi_listhead); | ||||
pcbinfo->ipi_count = 0; | pcbinfo->ipi_count = 0; | ||||
pcbinfo->ipi_hashbase = hashinit(hash_nelements, M_PCB, | pcbinfo->ipi_hashbase = hashinit(hash_nelements, M_PCB, | ||||
&pcbinfo->ipi_hashmask); | &pcbinfo->ipi_hashmask); | ||||
pcbinfo->ipi_porthashbase = hashinit(porthash_nelements, M_PCB, | pcbinfo->ipi_porthashbase = hashinit(porthash_nelements, M_PCB, | ||||
&pcbinfo->ipi_porthashmask); | &pcbinfo->ipi_porthashmask); | ||||
#ifdef PCBGROUP | #ifdef PCBGROUP | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (pcbinfo == &V_tcbinfo) { | ||||
INP_INFO_WLOCK_ASSERT(pcbinfo); | INP_INFO_WLOCK_ASSERT(pcbinfo); | ||||
} | } | ||||
#endif | #endif | ||||
error = 0; | error = 0; | ||||
inp = uma_zalloc(pcbinfo->ipi_zone, M_NOWAIT); | inp = uma_zalloc(pcbinfo->ipi_zone, M_NOWAIT); | ||||
if (inp == NULL) | if (inp == NULL) | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
if ((inp->inp_ebr_entry = ebr_epoch_entry_alloc(M_NOWAIT)) == NULL) { | |||||
uma_zfree(pcbinfo->ipi_zone, inp); | |||||
return (ENOBUFS); | |||||
} | |||||
bzero(inp, inp_zero_size); | bzero(inp, inp_zero_size); | ||||
inp->inp_pcbinfo = pcbinfo; | inp->inp_pcbinfo = pcbinfo; | ||||
inp->inp_socket = so; | inp->inp_socket = so; | ||||
inp->inp_cred = crhold(so->so_cred); | inp->inp_cred = crhold(so->so_cred); | ||||
inp->inp_inc.inc_fibnum = so->so_fibnum; | inp->inp_inc.inc_fibnum = so->so_fibnum; | ||||
#ifdef MAC | #ifdef MAC | ||||
error = mac_inpcb_init(inp, M_NOWAIT); | error = mac_inpcb_init(inp, M_NOWAIT); | ||||
if (error != 0) | if (error != 0) | ||||
Show All 24 Lines | |||||
#ifdef INET6 | #ifdef INET6 | ||||
if (V_ip6_auto_flowlabel) | if (V_ip6_auto_flowlabel) | ||||
inp->inp_flags |= IN6P_AUTOFLOWLABEL; | inp->inp_flags |= IN6P_AUTOFLOWLABEL; | ||||
#endif | #endif | ||||
inp->inp_gencnt = ++pcbinfo->ipi_gencnt; | inp->inp_gencnt = ++pcbinfo->ipi_gencnt; | ||||
refcount_init(&inp->inp_refcount, 1); /* Reference from inpcbinfo */ | refcount_init(&inp->inp_refcount, 1); /* Reference from inpcbinfo */ | ||||
INP_LIST_WUNLOCK(pcbinfo); | INP_LIST_WUNLOCK(pcbinfo); | ||||
#if defined(IPSEC) || defined(MAC) | #if defined(IPSEC) || defined(MAC) | ||||
out: | out: | ||||
if (error != 0) { | if (error != 0) { | ||||
crfree(inp->inp_cred); | crfree(inp->inp_cred); | ||||
uma_zfree(pcbinfo->ipi_zone, inp); | uma_zfree(pcbinfo->ipi_zone, inp); | ||||
} | } | ||||
#endif | #endif | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 824 Lines • ▼ Show 20 Lines | |||||
in_pcbref(struct inpcb *inp) | in_pcbref(struct inpcb *inp) | ||||
{ | { | ||||
KASSERT(inp->inp_refcount > 0, ("%s: refcount 0", __func__)); | KASSERT(inp->inp_refcount > 0, ("%s: refcount 0", __func__)); | ||||
refcount_acquire(&inp->inp_refcount); | refcount_acquire(&inp->inp_refcount); | ||||
} | } | ||||
static void | |||||
inp_deferred_free(void *cookie) | |||||
{ | |||||
struct inpcb *inp = cookie; | |||||
struct inpcbinfo *pcbinfo; | |||||
pcbinfo = inp->inp_pcbinfo; | |||||
ebr_epoch_entry_free(inp->inp_ebr_entry); | |||||
uma_zfree(pcbinfo->ipi_zone, inp); | |||||
} | |||||
/* | /* | ||||
* Drop a refcount on an inpcb elevated using in_pcbref(); because a call to | * Drop a refcount on an inpcb elevated using in_pcbref(); because a call to | ||||
* in_pcbfree() may have been made between in_pcbref() and in_pcbrele(), we | * in_pcbfree() may have been made between in_pcbref() and in_pcbrele(), we | ||||
* return a flag indicating whether or not the inpcb remains valid. If it is | * return a flag indicating whether or not the inpcb remains valid. If it is | ||||
* valid, we return with the inpcb lock held. | * valid, we return with the inpcb lock held. | ||||
* | * | ||||
* Notice that, unlike in_pcbref(), the inpcb lock must be held to drop a | * Notice that, unlike in_pcbref(), the inpcb lock must be held to drop a | ||||
* reference on an inpcb. Historically more work was done here (actually, in | * reference on an inpcb. Historically more work was done here (actually, in | ||||
* in_pcbfree_internal()) but has been moved to in_pcbfree() to avoid the | * in_pcbfree_internal()) but has been moved to in_pcbfree() to avoid the | ||||
* need for the pcbinfo lock in in_pcbrele(). Deferring the free is entirely | * need for the pcbinfo lock in in_pcbrele(). Deferring the free is entirely | ||||
* about memory stability (and continued use of the write lock). | * about memory stability (and continued use of the write lock). | ||||
*/ | */ | ||||
int | |||||
in_pcbrele_rlocked(struct inpcb *inp) | static inline void | ||||
in_pcb_safe_free(struct inpcb *inp) | |||||
{ | { | ||||
struct inpcbinfo *pcbinfo; | struct inpcbinfo *pcbinfo; | ||||
pcbinfo = inp->inp_pcbinfo; | |||||
if (curthread->td_pflags & TDP_ITHREAD) { | |||||
ebr_epoch_entry_init(V_tcbinfo.ipi_epoch, inp->inp_ebr_entry, inp, false); | |||||
ebr_epoch_defer(V_tcbinfo.ipi_epoch, inp->inp_ebr_entry, inp_deferred_free); | |||||
return; | |||||
} | |||||
INP_INFO_EBR_SYNCHRONIZE(&V_tcbinfo); | |||||
ebr_epoch_entry_free(inp->inp_ebr_entry); | |||||
hselasky: ebr_epoch_synchronize() can invoke pause() which needs a sleeping context, per D7017 which is… | |||||
Not Done Inline ActionsGood point. Some last minute API changes and a late night led to at least one oversight. I'll fix and ensure that free is always deferred in an ithread. kmacy: Good point. Some last minute API changes and a late night led to at least one oversight. I'll… | |||||
uma_zfree(pcbinfo->ipi_zone, inp); | |||||
} | |||||
int | |||||
in_pcbrele_rlocked(struct inpcb *inp) | |||||
{ | |||||
KASSERT(inp->inp_refcount > 0, ("%s: refcount 0", __func__)); | KASSERT(inp->inp_refcount > 0, ("%s: refcount 0", __func__)); | ||||
INP_RLOCK_ASSERT(inp); | INP_RLOCK_ASSERT(inp); | ||||
if (refcount_release(&inp->inp_refcount) == 0) { | if (refcount_release(&inp->inp_refcount) == 0) { | ||||
/* | /* | ||||
* If the inpcb has been freed, let the caller know, even if | * If the inpcb has been freed, let the caller know, even if | ||||
* this isn't the last reference. | * this isn't the last reference. | ||||
*/ | */ | ||||
if (inp->inp_flags2 & INP_FREED) { | if (inp->inp_flags2 & INP_FREED) { | ||||
INP_RUNLOCK(inp); | INP_RUNLOCK(inp); | ||||
return (1); | return (1); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__)); | KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__)); | ||||
INP_RUNLOCK(inp); | INP_RUNLOCK(inp); | ||||
pcbinfo = inp->inp_pcbinfo; | in_pcb_safe_free(inp); | ||||
uma_zfree(pcbinfo->ipi_zone, inp); | |||||
return (1); | return (1); | ||||
} | } | ||||
int | int | ||||
in_pcbrele_wlocked(struct inpcb *inp) | in_pcbrele_wlocked(struct inpcb *inp) | ||||
{ | { | ||||
struct inpcbinfo *pcbinfo; | |||||
KASSERT(inp->inp_refcount > 0, ("%s: refcount 0", __func__)); | KASSERT(inp->inp_refcount > 0, ("%s: refcount 0", __func__)); | ||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
if (refcount_release(&inp->inp_refcount) == 0) { | if (refcount_release(&inp->inp_refcount) == 0) { | ||||
/* | /* | ||||
* If the inpcb has been freed, let the caller know, even if | * If the inpcb has been freed, let the caller know, even if | ||||
* this isn't the last reference. | * this isn't the last reference. | ||||
*/ | */ | ||||
if (inp->inp_flags2 & INP_FREED) { | if (inp->inp_flags2 & INP_FREED) { | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (1); | return (1); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__)); | KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__)); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
pcbinfo = inp->inp_pcbinfo; | in_pcb_safe_free(inp); | ||||
uma_zfree(pcbinfo->ipi_zone, inp); | |||||
return (1); | return (1); | ||||
} | } | ||||
/* | /* | ||||
* Temporary wrapper. | * Temporary wrapper. | ||||
*/ | */ | ||||
int | int | ||||
in_pcbrele(struct inpcb *inp) | in_pcbrele(struct inpcb *inp) | ||||
▲ Show 20 Lines • Show All 1,436 Lines • Show Last 20 Lines |
ebr_epoch_synchronize() can invoke pause() which needs a sleeping context, per D7017 which is not allowed for the context from which in_pcb_safe_free() is called ???