> -----Original Message-----
> From: Andre Brait <[email protected]>
> Sent: Saturday, October 24, 2026 2:47 AM
> To: [email protected]
> Cc: [email protected]; Nguyen, Anthony L
> <[email protected]>; Kitszel, Przemyslaw
> <[email protected]>; [email protected];
> [email protected]; Andre Brait <[email protected]>
> Subject: [PATCH iwl-net v3] e1000e: work around TSO erratum that
> corrupts data and hangs Tx
> 
> On I219, a TSO packet whose first buffer is long and has a 4 KB boundary
> just after the TCP checksum field makes the NIC send corrupted data and
> then hang the Tx DMA. Forwarded VM traffic (tun/vhost-net) produces such
> packets, so hosts bridging guests onto an I219 hit it within minutes:
> 
>   e1000e 0000:00:1f.6 eno1: Detected Hardware Unit Hang
> 
> Right before the hang the receiver gets TCP segments carrying data from
> 128 bytes earlier in the stream, with a valid checksum.
> 
> This is the erratum Intel documents for the 82571/82572 (#76), 82574
> (#17) and 82583V (#7): if the first buffer is larger than
> 3 * (max_read_request - 4) bytes and a 4 KB boundary falls just after
> the headers, a read is repeated, wrong data goes out with a correct
> checksum, and the Tx DMA hangs. e1000e never implemented the documented
> workaround.
> 
> Apply it to all e1000e parts: limit the first data descriptor of a TSO
> packet to 3 * (max_read_request - 4) bytes (372 on the I219, where
> pcie_get_readrq() returns 128). The rest of the linear part goes into
> the next descriptor, and the NIC still segments the packet.
> 
> The split can take one descriptor more than before, which
> e1000_xmit_frame() already reserves: it counts one descriptor more than
> e1000_tx_map() uses, a leftover from e1000, which spends it on a 4-byte
> TSO sentinel that e1000e does not use. Comment that count.
> 
> Tested on I219-LM (8086:15bb), v7.0.14, byte-verified traffic:
> 
>                                         unpatched        patched
>   packets placed inside the window [1]  hang <12m        no hang
>   guest traffic, TCP timestamps on      hang <27m        60 min ok
>   guest traffic, TCP timestamps off     hang <7m         40 min ok
>   guest traffic, VLAN tag from stack    hang <20m        40 min ok
> 
>   [1] 785-1013 packets at each of 10 offsets, 54- to 70-byte headers
>   "hang <Xm": every run hung, the slowest within X minutes
> 
> Unpatched, data was corrupted at every hang. Patched, no corruption, no
> Tx timeouts or drops, and hardware TSO stayed in use (26.5M segments).
> 
> Developed with an AI coding assistant (Claude); details below the cut.
> 
> Fixes: bc7f75fa9788 ("[E1000E]: New pci-express e1000 driver (currently
> for ICH9 devices only)")
> Assisted-by: LLM
> Signed-off-by: Andre Brait <[email protected]>
> ---
> 
> Notes:
>     v3: incorporated the changes David Laight asked for in his review of
> v2:
>     a comment on the spare descriptor in e1000_xmit_frame() and a
> matching
>     paragraph in the commit message. The code itself does not change.
> 
>     v2: https://lore.kernel.org/netdev/20261020230211.75665-1-
> [email protected]/
>     v1: https://lore.kernel.org/netdev/20261018203215.77371-1-
> [email protected]/
> 
>     The investigation, the test results and how the AI assistant was
> used
>     are in the v2 notes and at
>     https://github.com/andrebrait/e1000e-i219-tso-hang/tree/3764faf44ab6
> 
>  drivers/net/ethernet/intel/e1000e/e1000.h  |  1 +
> drivers/net/ethernet/intel/e1000e/netdev.c | 14 ++++++++++++++
>  2 files changed, 15 insertions(+)
> 
> diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h
> b/drivers/net/ethernet/intel/e1000e/e1000.h
> index 63ebe0037..704284188 100644
> --- a/drivers/net/ethernet/intel/e1000e/e1000.h
> +++ b/drivers/net/ethernet/intel/e1000e/e1000.h
> @@ -219,6 +219,7 @@ struct e1000_adapter {
>       /* Tx - one ring per active queue */
>       struct e1000_ring *tx_ring ____cacheline_aligned_in_smp;
>       u32 tx_fifo_limit;
> +     u32 tx_hdr_buf_max;
> 
>       struct napi_struct napi;
> 
> diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c
> b/drivers/net/ethernet/intel/e1000e/netdev.c
> index 834a5b888..cb3b530d6 100644
> --- a/drivers/net/ethernet/intel/e1000e/netdev.c
> +++ b/drivers/net/ethernet/intel/e1000e/netdev.c
> @@ -4143,6 +4143,15 @@ void e1000e_reset(struct e1000_adapter *adapter)
>       adapter->tx_fifo_limit = min_t(u32, ((er32(PBA) >> 16) << 10) -
> 96,
>                                      24 << 10);
> 
> +     /* TSO erratum (82571/82572 #76, 82574 #17, 82583V #7, also seen
> on
> +      * I219): when the first buffer of a TSO packet is larger than
> +      * 3 * (max_read_request - 4) bytes and a 4 KB boundary lies just
> +      * after its headers, the NIC repeats a read, sends wrong data
> with a
> +      * correct checksum and then hangs. Keep the headers in a first
> buffer
> +      * no larger than that.
> +      */
> +     adapter->tx_hdr_buf_max = 3 * (pcie_get_readrq(adapter->pdev) -
> 4);
> +
>       /* Disable Adaptive Interrupt Moderation if 2 full packets cannot
>        * fit in receive buffer.
>        */
> @@ -5632,6 +5641,8 @@ static int e1000_tx_map(struct e1000_ring
> *tx_ring, struct sk_buff *skb,
>       while (len) {
>               buffer_info = &tx_ring->buffer_info[i];
>               size = min(len, max_per_txd);
> +             if (!offset && skb_is_gso(skb))
> +                     size = min(size, adapter->tx_hdr_buf_max);
> 
>               buffer_info->length = size;
>               buffer_info->time_stamp = jiffies;
> @@ -5905,6 +5916,9 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff
> *skb,
>       /* reserve a descriptor for the offload context */
>       if ((mss) || (skb->ip_summed == CHECKSUM_PARTIAL))
>               count++;
> +     /* spare descriptor: e1000_tx_map() splits the linear part of a
> long
> +      * TSO packet to keep its headers within tx_hdr_buf_max (TSO
> erratum)
> +      */
>       count++;
> 
>       count += DIV_ROUND_UP(len, adapter->tx_fifo_limit);
> --
> 2.56.0

Reviewed-by: Aleksandr Loktionov <[email protected]>

Reply via email to