diff -r 3774a7ad837d sys/netinet/tcp_input.c --- a/sys/netinet/tcp_input.c Fri Mar 18 17:27:05 2011 +1100 +++ b/sys/netinet/tcp_input.c Mon Mar 21 08:58:28 2011 +1100 @@ -1564,7 +1564,9 @@ } } else if (th->th_ack == tp->snd_una && tlen <= sbspace(&so->so_rcv)) { - int newsize = 0; /* automatic sockbuf scaling */ + int newsize, nrxbytes, nrxmaxsegs; + + newsize = 0; /* Automatic sockbuf scaling. */ /* * This is a pure, in-sequence data packet with @@ -1584,17 +1586,12 @@ if ((tp->t_flags & TF_SACK_PERMIT) && tp->rcv_numsacks) tcp_clean_sackreport(tp); TCPSTAT_INC(tcps_preddat); - tp->rcv_nxt += tlen; + /* * Pull snd_wl1 up to prevent seq wrap relative to * th_seq. */ tp->snd_wl1 = th->th_seq; - /* - * Pull rcv_up up to prevent seq wrap relative to - * rcv_nxt. - */ - tp->rcv_up = tp->rcv_nxt; TCPSTAT_INC(tcps_rcvpack); TCPSTAT_ADD(tcps_rcvbyte, tlen); ND6_HINT(tp); /* Some progress has been made */ @@ -1675,12 +1672,37 @@ } /* NB: sorwakeup_locked() does an implicit unlock. */ sorwakeup_locked(so); - if (DELAY_ACK(tp)) { - tp->t_flags |= TF_DELACK; - } else { - tp->t_flags |= TF_ACKNOW; - tcp_output(tp); - } + + nrxbytes = tlen; + nrxmaxsegs = tlen / tp->t_maxseg; + + /* For LRO, we need to send multiple ACKs. */ + do { + if (nrxmaxsegs < 2) { + /* Regular segment. */ + tp->rcv_nxt += nrxbytes; + } else { + /* LRO segment. */ + tp->rcv_nxt += V_tcp_delack_enabled ? + 2 * tp->t_maxseg : tp->t_maxseg; + nrxbytes -= V_tcp_delack_enabled ? + 2 * tp->t_maxseg : tp->t_maxseg; + } + + if (nrxmaxsegs < 2 && DELAY_ACK(tp)) { + tp->t_flags |= TF_DELACK; + } else { + tp->t_flags |= TF_ACKNOW; + tcp_output(tp); + } + + nrxmaxsegs -= V_tcp_delack_enabled ? 2 : 1; + + } while (nrxmaxsegs > 0); + + /* Prevent seq wrap relative to rcv_nxt. */ + tp->rcv_up = tp->rcv_nxt; + goto check_delack; } }