From: Alexander Bluhm Subject: Re: Enable ix(4) softLRO for IPv6 traffic To: tech@openbsd.org Cc: Richard von Seck Date: Mon, 8 Jun 2026 18:09:37 +0200 On Mon, Jun 08, 2026 at 08:56:20AM -0600, Theo de Raadt wrote: > Richard von Seck wrote: > > > this patch enables software large receive offload (LRO) for ix(4) > > devices on IPv6 traffic. IPv6 traffic is identified and then forwarded > > to the existing TCP softLRO implementation as e.g., already used in > > ixl(4). > > I'm scared of this, because the ixl(4) LRO enabling story has not been > pleasant. Many ixl(4) firmwares did thing wrong, and it has taken a long > time to cope with those bugs. The ix(4) product is generally older > and may have some of the same bugs. Yes, this may trigger bugs. We have to make softlro reliable first. Problem with this kind of LRO is, after forwarding to another interface, TSO might fail to handle DMA. So it should not to be commited now. > So I think it is fair to ask how many unique ix(4) models was this tested > on? I will run the diff on X550T, 82599, 82598AF. And E610 has been added to ix(4) recently, it should not be forgotten. Whitespace got mangled, here is the correct diff for the archive. bluhm diff --git sys/dev/pci/if_ix.c sys/dev/pci/if_ix.c index 0ef30ea6355..5a41f8cc455 100644 --- sys/dev/pci/if_ix.c +++ sys/dev/pci/if_ix.c @@ -3279,14 +3279,15 @@ ixgbe_rxeof(struct ix_rxring *rxr) struct ix_softc *sc = rxr->sc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + struct mbuf_list mltcp6 = MBUF_LIST_INITIALIZER(); struct mbuf *mp, *sendmp; uint8_t eop = 0; - uint16_t len, vtag; + uint16_t len, vtag, ptype; uint32_t staterr = 0; struct ixgbe_rx_buf *rxbuf, *nxbuf; union ixgbe_adv_rx_desc *rxdesc; size_t dsize = sizeof(union ixgbe_adv_rx_desc); - int i, nextp, rsccnt; + int i, nextp, rsccnt, livelocked; if (!ISSET(ifp->if_flags, IFF_RUNNING)) return FALSE; @@ -3322,9 +3323,15 @@ ixgbe_rxeof(struct ix_rxring *rxr) vtag = letoh16(rxdesc->wb.upper.vlan); eop = ((staterr & IXGBE_RXD_STAT_EOP) != 0); hash = lemtoh32(&rxdesc->wb.lower.hi_dword.rss); - hashtype = - lemtoh16(&rxdesc->wb.lower.lo_dword.hs_rss.pkt_info) & - IXGBE_RXDADV_RSSTYPE_MASK; + hashtype = ptype = + lemtoh16(&rxdesc->wb.lower.lo_dword.hs_rss.pkt_info); + hashtype &= IXGBE_RXDADV_RSSTYPE_MASK; + ptype &= IXGBE_RXDADV_PKTTYPE_MASK; + + /* + * since the NIC doesn't support RSC for IPv6 packets, rsccnt + * should be read as zero for incoming IPv6 traffic + */ rsccnt = lemtoh32(&rxdesc->wb.lower.lo_dword.data) & IXGBE_RXDADV_RSCCNT_MASK; rsccnt >>= IXGBE_RXDADV_RSCCNT_SHIFT; @@ -3408,7 +3415,11 @@ ixgbe_rxeof(struct ix_rxring *rxr) SET(sendmp->m_pkthdr.csum_flags, M_FLOWID); } - ml_enqueue(&ml, sendmp); + if (ISSET(ifp->if_xflags, IFXF_LRO) && + ISSET(ptype, IXGBE_RXDADV_PKTTYPE_IPV6)) + tcp_softlro_glue(&mltcp6, sendmp, ifp); + else + ml_enqueue(&ml, sendmp); } next_desc: if_rxr_put(&rxr->rx_ring, 1); @@ -3422,7 +3433,12 @@ next_desc: } rxr->next_to_check = i; + livelocked = 0; + if (ifiq_input(rxr->ifiq, &mltcp6)) + livelocked = 1; if (ifiq_input(rxr->ifiq, &ml)) + livelocked = 1; + if (livelocked) if_rxr_livelocked(&rxr->rx_ring); if (!(staterr & IXGBE_RXD_STAT_DD))