Index: sys/netinet/tcp_reass.c =================================================================== --- sys/netinet/tcp_reass.c (revision 225576) +++ sys/netinet/tcp_reass.c (working copy) @@ -176,6 +176,7 @@ tcp_reass(struct tcpcb *tp, struct tcphdr *th, int struct tseg_qent *p = NULL; struct tseg_qent *nq; struct tseg_qent *te = NULL; + struct tseg_qent tqs; struct socket *so = tp->t_inpcb->inp_socket; int flags; @@ -219,15 +220,20 @@ tcp_reass(struct tcpcb *tp, struct tcphdr *th, int } /* - * Allocate a new queue entry. If we can't, or hit the zone limit - * just drop the pkt. + * Allocate a new queue entry. If we can't (memory pressure or zone + * limit reached), either drop the packet or use a stack allocated + * backup structure if it is the segment we needed to plug the hole + * (th_seq == rcv_nxt). Handling the latter as a special case avoids + * connection stalls. */ te = uma_zalloc(V_tcp_reass_zone, M_NOWAIT); - if (te == NULL) { + if (te == NULL && th->th_seq != tp->rcv_nxt) { TCPSTAT_INC(tcps_rcvmemdrop); m_freem(m); *tlenp = 0; return (0); + } else if (th->th_seq == tp->rcv_nxt) { + te = &tqs; } tp->t_segqlen++; @@ -247,6 +253,9 @@ tcp_reass(struct tcpcb *tp, struct tcphdr *th, int */ if (p != NULL) { int i; + + KASSERT((te != &tqs), ("Important TCP reass assumption broke: " + "p != NULL && te == &tqs")); /* conversion to int (in i) handles seq wraparound */ i = p->tqe_th->th_seq + p->tqe_len - th->th_seq; if (i > 0) { @@ -304,6 +313,8 @@ tcp_reass(struct tcpcb *tp, struct tcphdr *th, int if (p == NULL) { LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q); } else { + KASSERT((te != &tqs), ("Important TCP reass assumption broke: " + "te == &tqs but is not being inserted at head of list.")); LIST_INSERT_AFTER(p, te, tqe_q); } @@ -312,8 +323,11 @@ present: * Present data to user, advancing rcv_nxt through * completed sequence space. */ - if (!TCPS_HAVEESTABLISHED(tp->t_state)) + if (!TCPS_HAVEESTABLISHED(tp->t_state)) { + KASSERT((te != &tqs), ("Important TCP reass assumption broke: " + "te == &tqs && !TCPS_HAVEESTABLISHED.")); return (0); + } q = LIST_FIRST(&tp->t_segq); if (!q || q->tqe_th->th_seq != tp->rcv_nxt) return (0); @@ -327,7 +341,8 @@ present: m_freem(q->tqe_m); else sbappendstream_locked(&so->so_rcv, q->tqe_m); - uma_zfree(V_tcp_reass_zone, q); + if (q != &tqs) + uma_zfree(V_tcp_reass_zone, q); tp->t_segqlen--; q = nq; } while (q && q->tqe_th->th_seq == tp->rcv_nxt);