From: jan@openbsd.org Subject: vmx(4): TCP/UDP checksum offloading To: tech@openbsd.org Date: Fri, 9 Feb 2024 22:04:37 +0100 Hi, This diff implements the TCP/UDP checksum offloading for vmx(4). I tested it on ESXi 8.0 with OpenBSD and Linux guests. ok? bye, Jan Index: dev/pci/if_vmx.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_vmx.c,v diff -u -p -r1.80 if_vmx.c --- dev/pci/if_vmx.c 9 Feb 2024 15:22:41 -0000 1.80 +++ dev/pci/if_vmx.c 9 Feb 2024 19:44:51 -0000 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -400,10 +401,12 @@ vmxnet3_attach(struct device *parent, st ifp->if_watchdog = vmxnet3_watchdog; ifp->if_hardmtu = VMXNET3_MAX_MTU; ifp->if_capabilities = IFCAP_VLAN_MTU; -#if 0 - if (sc->sc_ds->upt_features & UPT1_F_CSUM) + + if (sc->sc_ds->upt_features & UPT1_F_CSUM) { ifp->if_capabilities |= IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4; -#endif + ifp->if_capabilities |= IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6; + } + #if NVLAN > 0 if (sc->sc_ds->upt_features & UPT1_F_VLAN) ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; @@ -1398,6 +1401,57 @@ vmx_load_mbuf(bus_dma_tag_t dmat, bus_dm } void +vmxnet3_tx_offload(struct vmxnet3_txdesc *sop, struct mbuf *m) +{ + struct ether_extracted ext; + uint32_t offset = 0; + uint32_t hdrlen; + + /* + * VLAN Offload + */ + +#if NVLAN > 0 + if (ISSET(m->m_flags, M_VLANTAG)) { + sop->tx_word3 |= htole32(VMXNET3_TX_VTAG_MODE); + sop->tx_word3 |= htole32((m->m_pkthdr.ether_vtag & + VMXNET3_TX_VLANTAG_M) << VMXNET3_TX_VLANTAG_S); + } +#endif + + /* + * Checksum Offload + */ + + if (!ISSET(m->m_pkthdr.csum_flags, M_TCP_CSUM_OUT) && + !ISSET(m->m_pkthdr.csum_flags, M_UDP_CSUM_OUT)) + return; + + ether_extract_headers(m, &ext); + + hdrlen = sizeof(*ext.eh); + if (ext.evh) + hdrlen = sizeof(*ext.evh); + + if (ext.ip4) + hdrlen += ext.ip4->ip_hl << 2; + else if (ext.ip6) + hdrlen += sizeof(*ext.ip6); + + if (ext.tcp) + offset = hdrlen + offsetof(struct tcphdr, th_sum); + else if (ext.udp) + offset = hdrlen + offsetof(struct udphdr, uh_sum); + + hdrlen &= VMXNET3_TX_HLEN_M; + offset &= VMXNET3_TX_OP_M; + + sop->tx_word3 |= htole32(VMXNET3_OM_CSUM << VMXNET3_TX_OM_S); + sop->tx_word3 |= htole32(hdrlen << VMXNET3_TX_HLEN_S); + sop->tx_word2 |= htole32(offset << VMXNET3_TX_OP_S); +} + +void vmxnet3_start(struct ifqueue *ifq) { struct ifnet *ifp = ifq->ifq_if; @@ -1469,13 +1523,7 @@ vmxnet3_start(struct ifqueue *ifq) } txd->tx_word3 = htole32(VMXNET3_TX_EOP | VMXNET3_TX_COMPREQ); -#if NVLAN > 0 - if (ISSET(m->m_flags, M_VLANTAG)) { - sop->tx_word3 |= htole32(VMXNET3_TX_VTAG_MODE); - sop->tx_word3 |= htole32((m->m_pkthdr.ether_vtag & - VMXNET3_TX_VLANTAG_M) << VMXNET3_TX_VLANTAG_S); - } -#endif + vmxnet3_tx_offload(sop, m); ring->prod = prod; /* Change the ownership by flipping the "generation" bit */