From: Jan Klemkow Subject: iavf(4): add checksum offloading To: tech@openbsd.org Date: Thu, 31 Oct 2024 21:27:07 +0100 Hi, This diff add checksum offloading support for iavf(4). Its implemented similar to ixl(4). iavf(4) is just the virtual functions driver for ixl(4) interfaces. I tested it on Linux/KVM with Intel x710 interfaces. More tests on different environments and hardware are welcome! bye, Jan Index: dev/pci/if_iavf.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iavf.c,v diff -u -p -r1.17 if_iavf.c --- dev/pci/if_iavf.c 10 Jul 2024 09:50:28 -0000 1.17 +++ dev/pci/if_iavf.c 31 Oct 2024 19:55:40 -0000 @@ -49,6 +49,7 @@ */ #include "bpfilter.h" +#include "vlan.h" #include #include @@ -75,6 +76,7 @@ #include #include +#include #include #include @@ -890,11 +892,13 @@ iavf_attach(struct device *parent, struc strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ); ifq_init_maxlen(&ifp->if_snd, sc->sc_tx_ring_ndescs); - ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING; -#if 0 - ifp->if_capabilities |= IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | - IFCAP_CSUM_UDPv4; + ifp->if_capabilities = IFCAP_VLAN_MTU; +#if NVLAN > 0 + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; #endif + ifp->if_capabilities |= IFCAP_CSUM_IPv4 | + IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4 | + IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6; ifmedia_init(&sc->sc_media, 0, iavf_media_change, iavf_media_status); @@ -1656,6 +1660,57 @@ iavf_load_mbuf(bus_dma_tag_t dmat, bus_d BUS_DMA_STREAMING | BUS_DMA_NOWAIT)); } +static uint64_t +iavf_tx_offload(struct mbuf *m) +{ + struct ether_extracted ext; + uint64_t hlen; + uint64_t offload = 0; + +#if NVLAN > 0 + if (ISSET(m->m_flags, M_VLANTAG)) { + uint64_t vtag = m->m_pkthdr.ether_vtag; + offload |= IAVF_TX_DESC_CMD_IL2TAG1; + offload |= vtag << IAVF_TX_DESC_L2TAG1_SHIFT; + } +#endif + + if (!ISSET(m->m_pkthdr.csum_flags, + M_IPV4_CSUM_OUT|M_TCP_CSUM_OUT|M_UDP_CSUM_OUT)) + return (offload); + + ether_extract_headers(m, &ext); + + if (ext.ip4) { + offload |= ISSET(m->m_pkthdr.csum_flags, M_IPV4_CSUM_OUT) ? + IAVF_TX_DESC_CMD_IIPT_IPV4_CSUM : + IAVF_TX_DESC_CMD_IIPT_IPV4; +#ifdef INET6 + } else if (ext.ip6) { + offload |= IAVF_TX_DESC_CMD_IIPT_IPV6; +#endif + } else { + panic("CSUM_OUT set for non-IP packet"); + /* NOTREACHED */ + } + hlen = ext.iphlen; + + offload |= (ETHER_HDR_LEN >> 1) << IAVF_TX_DESC_MACLEN_SHIFT; + offload |= (hlen >> 2) << IAVF_TX_DESC_IPLEN_SHIFT; + + if (ext.tcp && ISSET(m->m_pkthdr.csum_flags, M_TCP_CSUM_OUT)) { + offload |= IAVF_TX_DESC_CMD_L4T_EOFT_TCP; + offload |= (uint64_t)(ext.tcphlen >> 2) + << IAVF_TX_DESC_L4LEN_SHIFT; + } else if (ext.udp && ISSET(m->m_pkthdr.csum_flags, M_UDP_CSUM_OUT)) { + offload |= IAVF_TX_DESC_CMD_L4T_EOFT_UDP; + offload |= (uint64_t)(sizeof(*ext.udp) >> 2) + << IAVF_TX_DESC_L4LEN_SHIFT; + } + + return offload; +} + static void iavf_start(struct ifqueue *ifq) { @@ -1667,7 +1722,7 @@ iavf_start(struct ifqueue *ifq) bus_dmamap_t map; struct mbuf *m; uint64_t cmd; - uint64_t vlan_cmd; + uint64_t offload; unsigned int prod, free, last, i; unsigned int mask; int post = 0; @@ -1702,6 +1757,8 @@ iavf_start(struct ifqueue *ifq) if (m == NULL) break; + offload = iavf_tx_offload(m); + txm = &txr->txr_maps[prod]; map = txm->txm_map; @@ -1714,20 +1771,13 @@ iavf_start(struct ifqueue *ifq) bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, BUS_DMASYNC_PREWRITE); - vlan_cmd = 0; - if (m->m_flags & M_VLANTAG) { - vlan_cmd = IAVF_TX_DESC_CMD_IL2TAG1 | - (((uint64_t)m->m_pkthdr.ether_vtag) << - IAVF_TX_DESC_L2TAG1_SHIFT); - } - for (i = 0; i < map->dm_nsegs; i++) { txd = &ring[prod]; cmd = (uint64_t)map->dm_segs[i].ds_len << IAVF_TX_DESC_BSIZE_SHIFT; - cmd |= IAVF_TX_DESC_DTYPE_DATA | IAVF_TX_DESC_CMD_ICRC | - vlan_cmd; + cmd |= IAVF_TX_DESC_DTYPE_DATA | IAVF_TX_DESC_CMD_ICRC; + cmd |= offload; htolem64(&txd->addr, map->dm_segs[i].ds_addr); htolem64(&txd->cmd, cmd); @@ -1938,6 +1988,24 @@ iavf_rxr_free(struct iavf_softc *sc, str free(rxr, M_DEVBUF, sizeof(*rxr)); } +static void +ixl_rx_checksum(struct mbuf *m, uint64_t word) +{ + if (!ISSET(word, IAVF_RX_DESC_L3L4P)) + return; + + if (ISSET(word, IAVF_RX_DESC_IPE)) + return; + + m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK; + + if (ISSET(word, IAVF_RX_DESC_L4E)) + return; + + m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK; +} + + static int iavf_rxeof(struct iavf_softc *sc, struct ifiqueue *ifiq) { @@ -2002,6 +2070,7 @@ iavf_rxeof(struct iavf_softc *sc, struct m->m_pkthdr.len += len; if (ISSET(word, IAVF_RX_DESC_EOP)) { +#if NVLAN > 0 if (ISSET(word, IAVF_RX_DESC_L2TAG1P)) { vlan = (lemtoh64(&rxd->qword0) & IAVF_RX_DESC_L2TAG1_MASK) @@ -2009,8 +2078,10 @@ iavf_rxeof(struct iavf_softc *sc, struct m->m_pkthdr.ether_vtag = vlan; m->m_flags |= M_VLANTAG; } +#endif if (!ISSET(word, IAVF_RX_DESC_RXE | IAVF_RX_DESC_OVERSIZE)) { + ixl_rx_checksum(m, word); ml_enqueue(&ml, m); } else { ifp->if_ierrors++; /* XXX */