Date: Wed, 27 Sep 2017 05:44:50 +0000 (UTC) From: Sepherosa Ziehau <sephe@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r324049 - head/sys/dev/hyperv/netvsc Message-ID: <201709270544.v8R5io50067311@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: sephe Date: Wed Sep 27 05:44:50 2017 New Revision: 324049 URL: https://svnweb.freebsd.org/changeset/base/324049 Log: hyperv/hn: Fix UDP checksum offload issue in Azure. UDP checksum offload does not work in Azure if following conditions are met: - sizeof(IP hdr + UDP hdr + payload) > 1420. - IP_DF is not set in IP hdr Use software checksum for UDP datagrams falling into this category. Add two tunables to disable UDP/IPv4 and UDP/IPv6 checksum offload, in case something unexpected happened. MFC after: 1 week Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D12429 Modified: head/sys/dev/hyperv/netvsc/if_hn.c Modified: head/sys/dev/hyperv/netvsc/if_hn.c ============================================================================== --- head/sys/dev/hyperv/netvsc/if_hn.c Wed Sep 27 04:42:40 2017 (r324048) +++ head/sys/dev/hyperv/netvsc/if_hn.c Wed Sep 27 05:44:50 2017 (r324049) @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/bus.h> +#include <sys/counter.h> #include <sys/kernel.h> #include <sys/limits.h> #include <sys/malloc.h> @@ -460,6 +461,35 @@ SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostip, CTLFLAG_RDT "Trust ip packet verification on host side, " "when csum info is missing (global setting)"); +/* + * Offload UDP/IPv4 checksum. + */ +static int hn_enable_udp4cs = 1; +SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp4cs, CTLFLAG_RDTUN, + &hn_enable_udp4cs, 0, "Offload UDP/IPv4 checksum"); + +/* + * Offload UDP/IPv6 checksum. + */ +static int hn_enable_udp6cs = 1; +SYSCTL_INT(_hw_hn, OID_AUTO, enable_udp6cs, CTLFLAG_RDTUN, + &hn_enable_udp6cs, 0, "Offload UDP/IPv6 checksum"); + +/* Stats. */ +static counter_u64_t hn_udpcs_fixup; +SYSCTL_COUNTER_U64(_hw_hn, OID_AUTO, udpcs_fixup, CTLFLAG_RW, + &hn_udpcs_fixup, "# of UDP checksum fixup"); + +/* + * See hn_set_hlen(). + * + * This value is for Azure. For Hyper-V, set this above + * 65536 to disable UDP datagram checksum fixup. + */ +static int hn_udpcs_fixup_mtu = 1420; +SYSCTL_INT(_hw_hn, OID_AUTO, udpcs_fixup_mtu, CTLFLAG_RWTUN, + &hn_udpcs_fixup_mtu, 0, "UDP checksum fixup MTU threshold"); + /* Limit TSO burst size */ static int hn_tso_maxlen = IP_MAXPACKET; SYSCTL_INT(_hw_hn, OID_AUTO, tso_maxlen, CTLFLAG_RDTUN, @@ -799,6 +829,27 @@ hn_set_hlen(struct mbuf *m_head) ip = mtodo(m_head, ehlen); iphlen = ip->ip_hl << 2; m_head->m_pkthdr.l3hlen = iphlen; + + /* + * UDP checksum offload does not work in Azure, if the + * following conditions meet: + * - sizeof(IP hdr + UDP hdr + payload) > 1420. + * - IP_DF is not set in the IP hdr. + * + * Fallback to software checksum for these UDP datagrams. + */ + if ((m_head->m_pkthdr.csum_flags & CSUM_IP_UDP) && + m_head->m_pkthdr.len > hn_udpcs_fixup_mtu + ehlen && + (ntohs(ip->ip_off) & IP_DF) == 0) { + uint16_t off = ehlen + iphlen; + + counter_u64_add(hn_udpcs_fixup, 1); + PULLUP_HDR(m_head, off + sizeof(struct udphdr)); + *(uint16_t *)(m_head->m_data + off + + m_head->m_pkthdr.csum_data) = in_cksum_skip( + m_head, m_head->m_pkthdr.len, off); + m_head->m_pkthdr.csum_flags &= ~CSUM_IP_UDP; + } } #endif #if defined(INET6) && defined(INET) @@ -5479,11 +5530,11 @@ hn_fixup_tx_data(struct hn_softc *sc) csum_assist |= CSUM_IP; if (sc->hn_caps & HN_CAP_TCP4CS) csum_assist |= CSUM_IP_TCP; - if (sc->hn_caps & HN_CAP_UDP4CS) + if ((sc->hn_caps & HN_CAP_UDP4CS) && hn_enable_udp4cs) csum_assist |= CSUM_IP_UDP; if (sc->hn_caps & HN_CAP_TCP6CS) csum_assist |= CSUM_IP6_TCP; - if (sc->hn_caps & HN_CAP_UDP6CS) + if ((sc->hn_caps & HN_CAP_UDP6CS) && hn_enable_udp6cs) csum_assist |= CSUM_IP6_UDP; for (i = 0; i < sc->hn_tx_ring_cnt; ++i) sc->hn_tx_ring[i].hn_csum_assist = csum_assist; @@ -7336,6 +7387,8 @@ hn_sysinit(void *arg __unused) { int i; + hn_udpcs_fixup = counter_u64_alloc(M_WAITOK); + #ifdef HN_IFSTART_SUPPORT /* * Don't use ifnet.if_start if transparent VF mode is requested; @@ -7415,5 +7468,7 @@ hn_sysuninit(void *arg __unused) if (hn_vfmap != NULL) free(hn_vfmap, M_DEVBUF); rm_destroy(&hn_vfmap_lock); + + counter_u64_free(hn_udpcs_fixup); } SYSUNINIT(hn_sysuninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_sysuninit, NULL);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201709270544.v8R5io50067311>