LRO: Fix lost packets when merging 1 payload with an ACK

To check if it needed to regenerate a packet's header before
sending it up the stack, LRO was checking if more than one payload
had been merged into the packet.  This failed in the case where
a single payload was merged with one or more pure ACKs.  This
results in lost ACKs.

Fix this by precisely tracking whether header regeneration is
required instead of using an incorrect heuristic.

Found with: Sysunit test
Differential Revision:	https://reviews.freebsd.org/D33774
Reviewed by: rrs
This commit is contained in:
Ryan Stone 2021-12-31 12:56:20 -05:00
parent 7d06c761c8
commit 24fe6643da
2 changed files with 14 additions and 5 deletions

View File

@ -820,7 +820,7 @@ static void
tcp_flush_out_entry(struct lro_ctrl *lc, struct lro_entry *le)
{
/* Check if we need to recompute any checksums. */
if (le->m_head->m_pkthdr.lro_nsegs > 1) {
if (le->needs_merge) {
uint16_t csum;
switch (le->inner.data.lro_type) {
@ -921,6 +921,7 @@ tcp_set_entry_to_mbuf(struct lro_ctrl *lc, struct lro_entry *le,
le->next_seq = ntohl(th->th_seq) + tcp_data_len;
le->ack_seq = th->th_ack;
le->window = th->th_win;
le->needs_merge = 0;
/* Setup new data pointers. */
le->m_head = m;
@ -962,10 +963,12 @@ tcp_push_and_replace(struct lro_ctrl *lc, struct lro_entry *le, struct mbuf *m)
}
static void
tcp_lro_mbuf_append_pkthdr(struct mbuf *m, const struct mbuf *p)
tcp_lro_mbuf_append_pkthdr(struct lro_entry *le, const struct mbuf *p)
{
struct mbuf *m;
uint32_t csum;
m = le->m_head;
if (m->m_pkthdr.lro_nsegs == 1) {
/* Compute relative checksum. */
csum = p->m_pkthdr.lro_tcp_d_csum;
@ -982,6 +985,7 @@ tcp_lro_mbuf_append_pkthdr(struct mbuf *m, const struct mbuf *p)
m->m_pkthdr.lro_tcp_d_csum = csum;
m->m_pkthdr.lro_tcp_d_len += p->m_pkthdr.lro_tcp_d_len;
m->m_pkthdr.lro_nsegs += p->m_pkthdr.lro_nsegs;
le->needs_merge = 1;
}
static void
@ -1100,8 +1104,12 @@ tcp_lro_condense(struct lro_ctrl *lc, struct lro_entry *le)
le->next_seq += tcp_data_len;
le->ack_seq = th->th_ack;
le->window = th->th_win;
le->needs_merge = 1;
} else if (th->th_ack == le->ack_seq) {
le->window = WIN_MAX(le->window, th->th_win);
if (WIN_GT(th->th_win, le->window)) {
le->window = th->th_win;
le->needs_merge = 1;
}
}
if (tcp_data_len == 0) {
@ -1110,7 +1118,7 @@ tcp_lro_condense(struct lro_ctrl *lc, struct lro_entry *le)
}
/* Merge TCP data checksum and length to head mbuf. */
tcp_lro_mbuf_append_pkthdr(le->m_head, m);
tcp_lro_mbuf_append_pkthdr(le, m);
/*
* Adjust the mbuf so that m_data points to the first byte of

View File

@ -146,7 +146,8 @@ struct lro_entry {
uint16_t compressed;
uint16_t uncompressed;
uint16_t window;
uint16_t timestamp; /* flag, not a TCP hdr field. */
uint16_t timestamp : 1;
uint16_t needs_merge : 1;
struct bintime alloc_time; /* time when entry was allocated */
};