Index: head/sys/dev/hyperv/netvsc/hv_net_vsc.h =================================================================== --- head/sys/dev/hyperv/netvsc/hv_net_vsc.h +++ head/sys/dev/hyperv/netvsc/hv_net_vsc.h @@ -244,6 +244,8 @@ uint32_t hn_rndis_rid; uint32_t hn_ndis_ver; + int hn_ndis_tso_szmax; + int hn_ndis_tso_sgmin; struct ndis_rssprm_toeplitz hn_rss; }; Index: head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c =================================================================== --- head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c +++ head/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c @@ -230,7 +230,7 @@ "when csum info is missing (global setting)"); /* Limit TSO burst size */ -static int hn_tso_maxlen = 0; +static int hn_tso_maxlen = IP_MAXPACKET; SYSCTL_INT(_hw_hn, OID_AUTO, tso_maxlen, CTLFLAG_RDTUN, &hn_tso_maxlen, 0, "TSO burst limit"); @@ -338,6 +338,7 @@ static int hn_create_rx_data(struct hn_softc *sc, int); static void hn_destroy_rx_data(struct hn_softc *sc); static void hn_set_chim_size(struct hn_softc *, int); +static void hn_set_tso_maxsize(struct hn_softc *, int, int); static int hn_chan_attach(struct hn_softc *, struct vmbus_channel *); static void hn_chan_detach(struct hn_softc *, struct vmbus_channel *); static int hn_attach_subchans(struct hn_softc *); @@ -520,7 +521,6 @@ uint32_t link_status; struct ifnet *ifp = NULL; int error, ring_cnt, tx_ring_cnt; - int tso_maxlen; sc->hn_dev = dev; sc->hn_prichan = vmbus_get_channel(dev); @@ -720,18 +720,16 @@ /* Enable all available capabilities by default. */ ifp->if_capenable = ifp->if_capabilities; - tso_maxlen = hn_tso_maxlen; - if (tso_maxlen <= 0 || tso_maxlen > IP_MAXPACKET) - tso_maxlen = IP_MAXPACKET; - ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX; - ifp->if_hw_tsomaxsegsize = PAGE_SIZE; - ifp->if_hw_tsomax = tso_maxlen - - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN); + if (ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) { + hn_set_tso_maxsize(sc, hn_tso_maxlen, ETHERMTU); + ifp->if_hw_tsomaxsegcount = HN_TX_DATA_SEGCNT_MAX; + ifp->if_hw_tsomaxsegsize = PAGE_SIZE; + } ether_ifattach(ifp, eaddr); - if (bootverbose) { - if_printf(ifp, "TSO: %u/%u/%u\n", ifp->if_hw_tsomax, + if ((ifp->if_capabilities & (IFCAP_TSO6 | IFCAP_TSO4)) && bootverbose) { + if_printf(ifp, "TSO segcnt %u segsz %u\n", ifp->if_hw_tsomaxsegcount, ifp->if_hw_tsomaxsegsize); } @@ -1672,6 +1670,7 @@ if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax) hn_set_chim_size(sc, sc->hn_chim_szmax); + hn_set_tso_maxsize(sc, hn_tso_maxlen, ifr->ifr_mtu); /* All done! Resume now. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) @@ -2919,6 +2918,34 @@ } static void +hn_set_tso_maxsize(struct hn_softc *sc, int tso_maxlen, int mtu) +{ + struct ifnet *ifp = sc->hn_ifp; + int tso_minlen; + + if ((ifp->if_capabilities & (IFCAP_TSO4 | IFCAP_TSO6)) == 0) + return; + + KASSERT(sc->hn_ndis_tso_sgmin >= 2, + ("invalid NDIS tso sgmin %d", sc->hn_ndis_tso_sgmin)); + tso_minlen = sc->hn_ndis_tso_sgmin * mtu; + + KASSERT(sc->hn_ndis_tso_szmax >= tso_minlen && + sc->hn_ndis_tso_szmax <= IP_MAXPACKET, + ("invalid NDIS tso szmax %d", sc->hn_ndis_tso_szmax)); + + if (tso_maxlen < tso_minlen) + tso_maxlen = tso_minlen; + else if (tso_maxlen > IP_MAXPACKET) + tso_maxlen = IP_MAXPACKET; + if (tso_maxlen > sc->hn_ndis_tso_szmax) + tso_maxlen = sc->hn_ndis_tso_szmax; + ifp->if_hw_tsomax = tso_maxlen - (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN); + if (bootverbose) + if_printf(ifp, "TSO size max %u\n", ifp->if_hw_tsomax); +} + +static void hn_fixup_tx_data(struct hn_softc *sc) { uint64_t csum_assist; @@ -3424,7 +3451,7 @@ /* * Attach RNDIS _after_ NVS is attached. */ - error = hn_rndis_attach(sc); + error = hn_rndis_attach(sc, mtu); if (error) return (error); Index: head/sys/dev/hyperv/netvsc/hv_rndis_filter.c =================================================================== --- head/sys/dev/hyperv/netvsc/hv_rndis_filter.c +++ head/sys/dev/hyperv/netvsc/hv_rndis_filter.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include #include @@ -77,6 +79,8 @@ NDIS_TXCSUM_CAP_IP6EXT) #define HN_NDIS_TXCSUM_CAP_UDP6 \ (NDIS_TXCSUM_CAP_UDP6 | NDIS_TXCSUM_CAP_IP6EXT) +#define HN_NDIS_LSOV2_CAP_IP6 \ + (NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT) /* * Forward declarations @@ -93,7 +97,7 @@ size_t min_odlen); static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen); -static int hn_rndis_conf_offload(struct hn_softc *sc); +static int hn_rndis_conf_offload(struct hn_softc *sc, int mtu); static int hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps); @@ -830,13 +834,13 @@ } static int -hn_rndis_conf_offload(struct hn_softc *sc) +hn_rndis_conf_offload(struct hn_softc *sc, int mtu) { struct ndis_offload hwcaps; struct ndis_offload_params params; uint32_t caps = 0; size_t paramsz; - int error; + int error, tso_maxsz, tso_minsg; error = hn_rndis_query_hwcaps(sc, &hwcaps); if (error) { @@ -857,18 +861,58 @@ } params.ndis_hdr.ndis_size = paramsz; - /* TSO */ + /* + * TSO4/TSO6 setup. + */ + tso_maxsz = IP_MAXPACKET; + tso_minsg = 2; if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) { caps |= HN_CAP_TSO4; params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON; - /* TODO: tso_max */ - } - if (hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) { + + if (hwcaps.ndis_lsov2.ndis_ip4_maxsz < tso_maxsz) + tso_maxsz = hwcaps.ndis_lsov2.ndis_ip4_maxsz; + if (hwcaps.ndis_lsov2.ndis_ip4_minsg > tso_minsg) + tso_minsg = hwcaps.ndis_lsov2.ndis_ip4_minsg; + } + if ((hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) && + (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6) == + HN_NDIS_LSOV2_CAP_IP6) { #ifdef notyet caps |= HN_CAP_TSO6; params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON; + + if (hwcaps.ndis_lsov2.ndis_ip6_maxsz < tso_maxsz) + tso_maxsz = hwcaps.ndis_lsov2.ndis_ip6_maxsz; + if (hwcaps.ndis_lsov2.ndis_ip6_minsg > tso_minsg) + tso_minsg = hwcaps.ndis_lsov2.ndis_ip6_minsg; #endif - /* TODO: tso_max */ + } + sc->hn_ndis_tso_szmax = 0; + sc->hn_ndis_tso_sgmin = 0; + if (caps & (HN_CAP_TSO4 | HN_CAP_TSO6)) { + KASSERT(tso_maxsz <= IP_MAXPACKET, + ("invalid NDIS TSO maxsz %d", tso_maxsz)); + KASSERT(tso_minsg >= 2, + ("invalid NDIS TSO minsg %d", tso_minsg)); + if (tso_maxsz < tso_minsg * mtu) { + if_printf(sc->hn_ifp, "invalid NDIS TSO config: " + "maxsz %d, minsg %d, mtu %d; " + "disable TSO4 and TSO6\n", + tso_maxsz, tso_minsg, mtu); + caps &= ~(HN_CAP_TSO4 | HN_CAP_TSO6); + params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_OFF; + params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_OFF; + } else { + sc->hn_ndis_tso_szmax = tso_maxsz; + sc->hn_ndis_tso_sgmin = tso_minsg; + if (bootverbose) { + if_printf(sc->hn_ifp, "NDIS TSO " + "szmax %d sgmin %d\n", + sc->hn_ndis_tso_szmax, + sc->hn_ndis_tso_sgmin); + } + } } /* IPv4 checksum */ @@ -1186,7 +1230,7 @@ } int -hn_rndis_attach(struct hn_softc *sc) +hn_rndis_attach(struct hn_softc *sc, int mtu) { int error; @@ -1201,7 +1245,7 @@ * Configure NDIS offload settings. * XXX no offloading, if error happened? */ - hn_rndis_conf_offload(sc); + hn_rndis_conf_offload(sc, mtu); return (0); } Index: head/sys/dev/hyperv/netvsc/if_hnvar.h =================================================================== --- head/sys/dev/hyperv/netvsc/if_hnvar.h +++ head/sys/dev/hyperv/netvsc/if_hnvar.h @@ -117,7 +117,7 @@ uint32_t hn_chim_alloc(struct hn_softc *sc); void hn_chim_free(struct hn_softc *sc, uint32_t chim_idx); -int hn_rndis_attach(struct hn_softc *sc); +int hn_rndis_attach(struct hn_softc *sc, int mtu); void hn_rndis_detach(struct hn_softc *sc); int hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags); void *hn_rndis_pktinfo_append(struct rndis_packet_msg *,