Index | Thread | Search

From:
jan@openbsd.org
Subject:
vmx(4): TCP/UDP checksum offloading
To:
tech@openbsd.org
Date:
Fri, 9 Feb 2024 22:04:37 +0100

Download raw body.

Thread
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 <netinet/in.h>
 #include <netinet/if_ether.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
 
@@ -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 */