Index | Thread | Search

From:
Jan Klemkow <jan@openbsd.org>
Subject:
iavf(4): add checksum offloading
To:
tech@openbsd.org
Date:
Thu, 31 Oct 2024 21:27:07 +0100

Download raw body.

Thread
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 <sys/param.h>
 #include <sys/systm.h>
@@ -75,6 +76,7 @@
 
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
+#include <netinet/udp.h>
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
@@ -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 */