Index: sys/kern/kern_mbuf.c =================================================================== --- sys/kern/kern_mbuf.c +++ sys/kern/kern_mbuf.c @@ -802,6 +802,13 @@ static void mb_reclaim(uma_zone_t zone __unused, int pending __unused) { + + mb_reclaim_from_all_domains(NULL); +} + +void +mb_reclaim_from_all_domains(struct ifnet *ifp) +{ struct domain *dp; struct protosw *pr; @@ -810,7 +817,7 @@ for (dp = domains; dp != NULL; dp = dp->dom_next) for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) if (pr->pr_drain != NULL) - (*pr->pr_drain)(); + (*pr->pr_drain)(ifp); } /* Index: sys/net/if.c =================================================================== --- sys/net/if.c +++ sys/net/if.c @@ -1206,6 +1206,13 @@ finish_vnet_shutdown: #endif /* + * Make sure all re-assembly mbufs get freed. This prevents + * references to our network interface structure after detach + * through mb->rcvif . + */ + mb_reclaim_from_all_domains(ifp); + + /* * We cannot hold the lock over dom_ifdetach calls as they might * sleep, for example trying to drain a callout, thus open up the * theoretical race with re-attaching. Index: sys/netinet/ip_input.c =================================================================== --- sys/netinet/ip_input.c +++ sys/netinet/ip_input.c @@ -93,7 +93,7 @@ /* IP reassembly functions are defined in ip_reass.c. */ extern void ipreass_init(void); -extern void ipreass_drain(void); +extern void ipreass_drain(struct ifnet *); extern void ipreass_slowtimo(void); #ifdef VIMAGE extern void ipreass_destroy(void); @@ -852,17 +852,24 @@ } void -ip_drain(void) +ip_drain(struct ifnet *ifp) { - VNET_ITERATOR_DECL(vnet_iter); - VNET_LIST_RLOCK_NOSLEEP(); - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); - ipreass_drain(); + if (ifp != NULL) { + CURVNET_SET(ifp->if_vnet); + ipreass_drain(ifp); CURVNET_RESTORE(); + } else { + VNET_ITERATOR_DECL(vnet_iter); + + VNET_LIST_RLOCK_NOSLEEP(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + ipreass_drain(NULL); + CURVNET_RESTORE(); + } + VNET_LIST_RUNLOCK_NOSLEEP(); } - VNET_LIST_RUNLOCK_NOSLEEP(); } /* Index: sys/netinet/ip_reass.c =================================================================== --- sys/netinet/ip_reass.c +++ sys/netinet/ip_reass.c @@ -88,7 +88,7 @@ #define V_ipreass_maxbucketsize VNET(ipreass_maxbucketsize) void ipreass_init(void); -void ipreass_drain(void); +void ipreass_drain(struct ifnet *); void ipreass_slowtimo(void); #ifdef VIMAGE void ipreass_destroy(void); @@ -592,7 +592,7 @@ * Drain off all datagram fragments. */ void -ipreass_drain(void) +ipreass_drain(struct ifnet *ifp) { for (int i = 0; i < IPREASS_NHASH; i++) { @@ -614,7 +614,7 @@ ipreass_destroy(void) { - ipreass_drain(); + ipreass_drain(NULL); uma_zdestroy(V_ipq_zone); for (int i = 0; i < IPREASS_NHASH; i++) mtx_destroy(&V_ipq[i].lock); @@ -712,7 +712,7 @@ V_noreass = 0; } else if (max == 0) { V_noreass = 1; - ipreass_drain(); + ipreass_drain(NULL); } else if (max == -1) { V_noreass = 0; uma_zone_set_max(V_ipq_zone, 0); Index: sys/netinet/ip_var.h =================================================================== --- sys/netinet/ip_var.h +++ sys/netinet/ip_var.h @@ -209,7 +209,7 @@ int inp_setmoptions(struct inpcb *, struct sockopt *); int ip_ctloutput(struct socket *, struct sockopt *sopt); -void ip_drain(void); +void ip_drain(struct ifnet *); int ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu, u_long if_hwassist_flags); void ip_forward(struct mbuf *m, int srcrt); Index: sys/netinet/sctp_pcb.h =================================================================== --- sys/netinet/sctp_pcb.h +++ sys/netinet/sctp_pcb.h @@ -619,7 +619,7 @@ int sctp_is_vtag_good(uint32_t, uint16_t lport, uint16_t rport, struct timeval *); -/* void sctp_drain(void); */ +/* void sctp_drain(struct ifnet *); */ int sctp_destination_is_reachable(struct sctp_tcb *, struct sockaddr *); Index: sys/netinet/sctp_pcb.c =================================================================== --- sys/netinet/sctp_pcb.c +++ sys/netinet/sctp_pcb.c @@ -7051,45 +7051,56 @@ */ } +static void +sctp_drain_sub(struct ifnet *ifp) +{ + struct sctp_inpcb *inp; + struct sctp_tcb *stcb; + + SCTP_STAT_INCR(sctps_protocol_drain_calls); + + if (ifp == NULL && + SCTP_BASE_SYSCTL(sctp_do_drain) == 0) + return; + + SCTP_INP_INFO_RLOCK(); + LIST_FOREACH(inp, &SCTP_BASE_INFO(listhead), sctp_list) { + /* For each endpoint */ + SCTP_INP_RLOCK(inp); + LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { + /* For each association */ + SCTP_TCB_LOCK(stcb); + sctp_drain_mbufs(stcb); + SCTP_TCB_UNLOCK(stcb); + } + SCTP_INP_RUNLOCK(inp); + } + SCTP_INP_INFO_RUNLOCK(); +} + void -sctp_drain() +sctp_drain(struct ifnet *ifp) { - /* - * We must walk the PCB lists for ALL associations here. The system - * is LOW on MBUF's and needs help. This is where reneging will - * occur. We really hope this does NOT happen! - */ - VNET_ITERATOR_DECL(vnet_iter); - VNET_LIST_RLOCK_NOSLEEP(); - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); - struct sctp_inpcb *inp; - struct sctp_tcb *stcb; - SCTP_STAT_INCR(sctps_protocol_drain_calls); - if (SCTP_BASE_SYSCTL(sctp_do_drain) == 0) { -#ifdef VIMAGE - continue; -#else - return; -#endif + if (ifp != NULL) { + CURVNET_SET(ifp->if_vnet); + sctp_drain_sub(ifp); + CURVNET_RESTORE(); + } else { + /* + * We must walk the PCB lists for ALL associations here. The system + * is LOW on MBUF's and needs help. This is where reneging will + * occur. We really hope this does NOT happen! + */ + VNET_ITERATOR_DECL(vnet_iter); + VNET_LIST_RLOCK_NOSLEEP(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + sctp_drain_sub(ifp); + CURVNET_RESTORE(); } - SCTP_INP_INFO_RLOCK(); - LIST_FOREACH(inp, &SCTP_BASE_INFO(listhead), sctp_list) { - /* For each endpoint */ - SCTP_INP_RLOCK(inp); - LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { - /* For each association */ - SCTP_TCB_LOCK(stcb); - sctp_drain_mbufs(stcb); - SCTP_TCB_UNLOCK(stcb); - } - SCTP_INP_RUNLOCK(inp); - } - SCTP_INP_INFO_RUNLOCK(); - CURVNET_RESTORE(); + VNET_LIST_RUNLOCK_NOSLEEP(); } - VNET_LIST_RUNLOCK_NOSLEEP(); } /* Index: sys/netinet/sctp_var.h =================================================================== --- sys/netinet/sctp_var.h +++ sys/netinet/sctp_var.h @@ -339,7 +339,7 @@ int sctp_input(struct mbuf **, int *, int); #endif void sctp_pathmtu_adjustment(struct sctp_tcb *, uint16_t); -void sctp_drain(void); +void sctp_drain(struct ifnet *); void sctp_init(void); void sctp_notify(struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *, Index: sys/netinet/tcp_subr.c =================================================================== --- sys/netinet/tcp_subr.c +++ sys/netinet/tcp_subr.c @@ -1994,20 +1994,12 @@ return (tp); } -void -tcp_drain(void) +static void +tcp_drain_sub(struct ifnet *ifp) { - VNET_ITERATOR_DECL(vnet_iter); + struct inpcb *inpb; + struct tcpcb *tcpb; - if (!do_tcpdrain) - return; - - VNET_LIST_RLOCK_NOSLEEP(); - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); - struct inpcb *inpb; - struct tcpcb *tcpb; - /* * Walk the tcpbs, if existing, and flush the reassembly queue, * if there is one... @@ -2016,33 +2008,55 @@ * where we're really low on mbufs, this is potentially * useful. */ - INP_INFO_WLOCK(&V_tcbinfo); - CK_LIST_FOREACH(inpb, V_tcbinfo.ipi_listhead, inp_list) { - INP_WLOCK(inpb); - if (inpb->inp_flags & INP_TIMEWAIT) { - INP_WUNLOCK(inpb); - continue; - } - if ((tcpb = intotcpcb(inpb)) != NULL) { - tcp_reass_flush(tcpb); - tcp_clean_sackreport(tcpb); + + INP_INFO_WLOCK(&V_tcbinfo); + CK_LIST_FOREACH(inpb, V_tcbinfo.ipi_listhead, inp_list) { + INP_WLOCK(inpb); + if (inpb->inp_flags & INP_TIMEWAIT) { + INP_WUNLOCK(inpb); + continue; + } + if ((tcpb = intotcpcb(inpb)) != NULL) { + tcp_reass_flush(tcpb); + tcp_clean_sackreport(tcpb); #ifdef TCP_BLACKBOX - tcp_log_drain(tcpb); + tcp_log_drain(tcpb); #endif #ifdef TCPPCAP - if (tcp_pcap_aggressive_free) { - /* Free the TCP PCAP queues. */ - tcp_pcap_drain(&(tcpb->t_inpkts)); - tcp_pcap_drain(&(tcpb->t_outpkts)); - } + if (tcp_pcap_aggressive_free) { + /* Free the TCP PCAP queues. */ + tcp_pcap_drain(&(tcpb->t_inpkts)); + tcp_pcap_drain(&(tcpb->t_outpkts)); + } #endif - } - INP_WUNLOCK(inpb); } - INP_INFO_WUNLOCK(&V_tcbinfo); + INP_WUNLOCK(inpb); + } + INP_INFO_WUNLOCK(&V_tcbinfo); +} + +void +tcp_drain(struct ifnet *ifp) +{ + + if (ifp != NULL) { + CURVNET_SET(ifp->if_vnet); + tcp_drain_sub(ifp); CURVNET_RESTORE(); + } else { + VNET_ITERATOR_DECL(vnet_iter); + + if (do_tcpdrain == 0) + return; + + VNET_LIST_RLOCK_NOSLEEP(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + tcp_drain_sub(NULL); + CURVNET_RESTORE(); + } + VNET_LIST_RUNLOCK_NOSLEEP(); } - VNET_LIST_RUNLOCK_NOSLEEP(); } /* Index: sys/netinet/tcp_var.h =================================================================== --- sys/netinet/tcp_var.h +++ sys/netinet/tcp_var.h @@ -836,7 +836,7 @@ int tcp_ctloutput(struct socket *, struct sockopt *); struct tcpcb * tcp_drop(struct tcpcb *, int); -void tcp_drain(void); +void tcp_drain(struct ifnet *); void tcp_init(void); void tcp_fini(void *); char *tcp_log_addrs(struct in_conninfo *, struct tcphdr *, void *, Index: sys/netinet6/frag6.c =================================================================== --- sys/netinet6/frag6.c +++ sys/netinet6/frag6.c @@ -911,33 +911,47 @@ VNET_LIST_RUNLOCK_NOSLEEP(); } +static void +frag6_drain_sub(struct ifnet *ifp) +{ + struct ip6q *head; + int i; + + for (i = 0; i < IP6REASS_NHASH; i++) { + if (IP6Q_TRYLOCK(i) == 0) + continue; + head = IP6Q_HEAD(i); + while (head->ip6q_next != head) { + IP6STAT_INC(ip6s_fragdropped); + /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ + frag6_freef(head->ip6q_next, i); + } + IP6Q_UNLOCK(i); + } +} + /* * Drain off all datagram fragments. */ void -frag6_drain(void) +frag6_drain(struct ifnet *ifp) { - VNET_ITERATOR_DECL(vnet_iter); - struct ip6q *head; - int i; - VNET_LIST_RLOCK_NOSLEEP(); - VNET_FOREACH(vnet_iter) { - CURVNET_SET(vnet_iter); - for (i = 0; i < IP6REASS_NHASH; i++) { - if (IP6Q_TRYLOCK(i) == 0) - continue; - head = IP6Q_HEAD(i); - while (head->ip6q_next != head) { - IP6STAT_INC(ip6s_fragdropped); - /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ - frag6_freef(head->ip6q_next, i); - } - IP6Q_UNLOCK(i); + if (ifp != NULL) { + CURVNET_SET(ifp->if_vnet); + frag6_drain_sub(ifp); + CURVNET_RESTORE(); + } else { + VNET_ITERATOR_DECL(vnet_iter); + + VNET_LIST_RLOCK_NOSLEEP(); + VNET_FOREACH(vnet_iter) { + CURVNET_SET(vnet_iter); + frag6_drain_sub(NULL); + CURVNET_RESTORE(); } - CURVNET_RESTORE(); + VNET_LIST_RUNLOCK_NOSLEEP(); } - VNET_LIST_RUNLOCK_NOSLEEP(); } int Index: sys/netinet6/ip6_var.h =================================================================== --- sys/netinet6/ip6_var.h +++ sys/netinet6/ip6_var.h @@ -418,7 +418,7 @@ void frag6_init(void); int frag6_input(struct mbuf **, int *, int); void frag6_slowtimo(void); -void frag6_drain(void); +void frag6_drain(struct ifnet *); void rip6_init(void); int rip6_input(struct mbuf **, int *, int); Index: sys/sys/mbuf.h =================================================================== --- sys/sys/mbuf.h +++ sys/sys/mbuf.h @@ -615,6 +615,7 @@ void mb_dupcl(struct mbuf *, struct mbuf *); void mb_free_ext(struct mbuf *); +void mb_reclaim_from_all_domains(struct ifnet *); void m_adj(struct mbuf *, int); int m_apply(struct mbuf *, int, int, int (*)(void *, void *, u_int), void *); Index: sys/sys/protosw.h =================================================================== --- sys/sys/protosw.h +++ sys/sys/protosw.h @@ -42,6 +42,7 @@ struct sockaddr; struct socket; struct sockopt; +struct ifnet; /*#ifdef _KERNEL*/ /* @@ -74,7 +75,7 @@ typedef void pr_init_t (void); typedef void pr_fasttimo_t (void); typedef void pr_slowtimo_t (void); -typedef void pr_drain_t (void); +typedef void pr_drain_t (struct ifnet *); struct protosw { short pr_type; /* socket type used for */