From 04206048d76a5e65850ef438acfece521d510cec Mon Sep 17 00:00:00 2001 From: dlg Date: Tue, 8 Feb 2022 11:55:19 +0000 Subject: [PATCH] bring back IPv4, TCP4/6 and UDP4/6 checksum offloading. this was first introduced in r1.176 by jan@. this diff includes two fixes to that implementation. the most important one is to parse the ip and tcp headers before a possible call to m_defrag. if an l4 offload is requested, it's only requested by the stack when the payload is correctly aligned and with each header contiguous in memory. this means you can use m_getptr and cast the packet data to the relevant headers to read them directly because that's what the stack does when it's working on them. this makes it cheap to work on them too. however, if you m_defrag, it ignores the alignment and ends up making it unsafe to dereference the ip and tcp/udp payloads on strict alignment architectures. if we want to look at the headers after m_defrag, we'd likely have to copy them onto the stack first. the other fix is to reset the offload bits between packets in the loop in ixl_start. another difference is that this code skips parsing the packet if no checksum offload is requested. tests and a tweak by bluhm@ to actually use the offloading tested by me on sparc64 and x86 boxes ok bluhm@ jmatthew@ --- sys/dev/pci/if_ixl.c | 97 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/sys/dev/pci/if_ixl.c b/sys/dev/pci/if_ixl.c index 83a68a62afc..b765aea50dc 100644 --- a/sys/dev/pci/if_ixl.c +++ b/sys/dev/pci/if_ixl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ixl.c,v 1.78 2022/01/09 05:42:54 jsg Exp $ */ +/* $OpenBSD: if_ixl.c,v 1.79 2022/02/08 11:55:19 dlg Exp $ */ /* * Copyright (c) 2013-2015, Intel Corporation @@ -82,6 +82,10 @@ #endif #include +#include +#include +#include +#include #include #include @@ -1942,9 +1946,10 @@ ixl_attach(struct device *parent, struct device *self, void *aux) ifp->if_capabilities = IFCAP_VLAN_MTU; #if 0 ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; - ifp->if_capabilities |= IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | - IFCAP_CSUM_UDPv4; #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, ixl_media_change, ixl_media_status); @@ -2771,6 +2776,88 @@ ixl_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m) BUS_DMA_STREAMING | BUS_DMA_NOWAIT)); } +static uint64_t +ixl_tx_setup_offload(struct mbuf *m0) +{ + struct mbuf *m; + int hoff; + uint64_t hlen; + uint8_t ipproto; + uint64_t offload = 0; + + if (!ISSET(m0->m_pkthdr.csum_flags, + M_IPV4_CSUM_OUT|M_TCP_CSUM_OUT|M_UDP_CSUM_OUT)) + return (0); + + switch (ntohs(mtod(m0, struct ether_header *)->ether_type)) { + case ETHERTYPE_IP: { + struct ip *ip; + + m = m_getptr(m0, ETHER_HDR_LEN, &hoff); + KASSERT(m != NULL && m->m_len - hoff >= sizeof(*ip)); + ip = (struct ip *)(mtod(m, caddr_t) + hoff); + + offload |= ISSET(m0->m_pkthdr.csum_flags, M_IPV4_CSUM_OUT) ? + IXL_TX_DESC_CMD_IIPT_IPV4_CSUM : + IXL_TX_DESC_CMD_IIPT_IPV4; + + hlen = ip->ip_hl << 2; + ipproto = ip->ip_p; + break; + } + +#ifdef INET6 + case ETHERTYPE_IPV6: { + struct ip6_hdr *ip6; + + m = m_getptr(m0, ETHER_HDR_LEN, &hoff); + KASSERT(m != NULL && m->m_len - hoff >= sizeof(*ip6)); + ip6 = (struct ip6_hdr *)(mtod(m, caddr_t) + hoff); + + offload |= IXL_TX_DESC_CMD_IIPT_IPV6; + + hlen = sizeof(*ip6); + ipproto = ip6->ip6_nxt; + break; + } +#endif + default: + panic("CSUM_OUT set for non-IP packet"); + /* NOTREACHED */ + } + + offload |= (ETHER_HDR_LEN >> 1) << IXL_TX_DESC_MACLEN_SHIFT; + offload |= (hlen >> 2) << IXL_TX_DESC_IPLEN_SHIFT; + + switch (ipproto) { + case IPPROTO_TCP: { + struct tcphdr *th; + + if (!ISSET(m0->m_pkthdr.csum_flags, M_TCP_CSUM_OUT)) + break; + + m = m_getptr(m, hoff + hlen, &hoff); + KASSERT(m != NULL && m->m_len - hoff >= sizeof(*th)); + th = (struct tcphdr *)(mtod(m, caddr_t) + hoff); + + offload |= IXL_TX_DESC_CMD_L4T_EOFT_TCP; + offload |= (uint64_t)th->th_off << IXL_TX_DESC_L4LEN_SHIFT; + break; + } + + case IPPROTO_UDP: + if (!ISSET(m0->m_pkthdr.csum_flags, M_UDP_CSUM_OUT)) + break; + + offload |= IXL_TX_DESC_CMD_L4T_EOFT_UDP; + offload |= (sizeof(struct udphdr) >> 2) << + IXL_TX_DESC_L4LEN_SHIFT; + break; + } + + return (offload); +} + static void ixl_start(struct ifqueue *ifq) { @@ -2785,6 +2872,7 @@ ixl_start(struct ifqueue *ifq) unsigned int prod, free, last, i; unsigned int mask; int post = 0; + uint64_t offload; #if NBPFILTER > 0 caddr_t if_bpf; #endif @@ -2816,6 +2904,8 @@ ixl_start(struct ifqueue *ifq) if (m == NULL) break; + offload = ixl_tx_setup_offload(m); + txm = &txr->txr_maps[prod]; map = txm->txm_map; @@ -2834,6 +2924,7 @@ ixl_start(struct ifqueue *ifq) cmd = (uint64_t)map->dm_segs[i].ds_len << IXL_TX_DESC_BSIZE_SHIFT; cmd |= IXL_TX_DESC_DTYPE_DATA | IXL_TX_DESC_CMD_ICRC; + cmd |= offload; htolem64(&txd->addr, map->dm_segs[i].ds_addr); htolem64(&txd->cmd, cmd); -- 2.20.1