summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorsephe <sephe@FreeBSD.org>2016-10-19 07:24:32 +0000
committersephe <sephe@FreeBSD.org>2016-10-19 07:24:32 +0000
commitdc46df7561ebe75f8bd3d5b89478eb8a17600384 (patch)
treee9c3cfb7296493ad624b69f9aa48f8b0c50229ed /sys
parentc82515f1657ed148dccf0f57652dadd19dbb73cc (diff)
downloadFreeBSD-src-dc46df7561ebe75f8bd3d5b89478eb8a17600384.zip
FreeBSD-src-dc46df7561ebe75f8bd3d5b89478eb8a17600384.tar.gz
MFC 306936-306939
306936 hyperv/hn: Fix checksum offload settings The _correct_ way to identify the supported checksum offloading and TSO parameters is to query OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES. Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8088 306937 hyperv/hn: Fix if_hw_tsomax setup. Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8089 306938 hyperv/hn: Generalize RSS capabilities query. - Support NDIS < 6.30. - Stringent response checks. Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8090 306939 hyperv/hn: Suffix NDIS offload size with NDIS version. Sponsored by: Microsoft Differential Revision: https://reviews.freebsd.org/D8091
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.h2
-rw-r--r--sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c51
-rw-r--r--sys/dev/hyperv/netvsc/hv_rndis_filter.c317
-rw-r--r--sys/dev/hyperv/netvsc/if_hnvar.h2
-rw-r--r--sys/dev/hyperv/netvsc/ndis.h124
-rw-r--r--sys/net/rndis.h1
6 files changed, 453 insertions, 44 deletions
diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h
index 30c3094..564af2e 100644
--- a/sys/dev/hyperv/netvsc/hv_net_vsc.h
+++ b/sys/dev/hyperv/netvsc/hv_net_vsc.h
@@ -244,6 +244,8 @@ struct hn_softc {
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;
};
diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
index 5331fbb..2db2453 100644
--- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
+++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
@@ -230,7 +230,7 @@ SYSCTL_INT(_hw_hn, OID_AUTO, trust_hostip, CTLFLAG_RDTUN,
"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_encap(struct hn_tx_ring *, struct hn_txdesc *, struct mbuf **);
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 @@ netvsc_attach(device_t dev)
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 @@ netvsc_attach(device_t dev)
/* 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 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
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 @@ hn_set_chim_size(struct hn_softc *sc, int chim_size)
}
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 @@ hn_synth_attach(struct hn_softc *sc, int mtu)
/*
* Attach RNDIS _after_ NVS is attached.
*/
- error = hn_rndis_attach(sc);
+ error = hn_rndis_attach(sc, mtu);
if (error)
return (error);
diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.c b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
index f6e6c5f..4af6e99 100644
--- a/sys/dev/hyperv/netvsc/hv_rndis_filter.c
+++ b/sys/dev/hyperv/netvsc/hv_rndis_filter.c
@@ -39,6 +39,8 @@ __FBSDID("$FreeBSD$");
#include <net/if_var.h>
#include <net/ethernet.h>
#include <net/rndis.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
#include <sys/types.h>
#include <machine/atomic.h>
#include <sys/sema.h>
@@ -68,6 +70,18 @@ __FBSDID("$FreeBSD$");
#define HN_RNDIS_XFER_SIZE 2048
+#define HN_NDIS_TXCSUM_CAP_IP4 \
+ (NDIS_TXCSUM_CAP_IP4 | NDIS_TXCSUM_CAP_IP4OPT)
+#define HN_NDIS_TXCSUM_CAP_TCP4 \
+ (NDIS_TXCSUM_CAP_TCP4 | NDIS_TXCSUM_CAP_TCP4OPT)
+#define HN_NDIS_TXCSUM_CAP_TCP6 \
+ (NDIS_TXCSUM_CAP_TCP6 | NDIS_TXCSUM_CAP_TCP6OPT | \
+ 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
*/
@@ -78,9 +92,14 @@ static void hv_rf_receive_data(struct hn_rx_ring *rxr,
static int hn_rndis_query(struct hn_softc *sc, uint32_t oid,
const void *idata, size_t idlen, void *odata, size_t *odlen0);
+static int hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
+ const void *idata, size_t idlen, void *odata, size_t *odlen0,
+ 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);
static __inline uint32_t
hn_rndis_rid(struct hn_softc *sc)
@@ -624,6 +643,15 @@ static int
hn_rndis_query(struct hn_softc *sc, uint32_t oid,
const void *idata, size_t idlen, void *odata, size_t *odlen0)
{
+
+ return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
+}
+
+static int
+hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
+ const void *idata, size_t idlen, void *odata, size_t *odlen0,
+ size_t min_odlen)
+{
struct rndis_query_req *req;
const struct rndis_query_comp *comp;
struct vmbus_xact *xact;
@@ -661,7 +689,7 @@ hn_rndis_query(struct hn_softc *sc, uint32_t oid,
memcpy(req + 1, idata, idlen);
}
- comp_len = sizeof(*comp) + odlen;
+ comp_len = sizeof(*comp) + min_odlen;
comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
REMOTE_NDIS_QUERY_CMPLT);
if (comp == NULL) {
@@ -717,26 +745,44 @@ hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt)
size_t caps_len;
int error;
- /*
- * Only NDIS 6.30+ is supported.
- */
- KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30,
- ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
*rxr_cnt = 0;
memset(&in, 0, sizeof(in));
in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
- in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
- in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
+ if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) {
+ in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_1;
+ in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE_6_0;
+ } else {
+ in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
+ in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
+ }
caps_len = NDIS_RSS_CAPS_SIZE;
- error = hn_rndis_query(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
- &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len);
+ error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
+ &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0);
if (error)
return (error);
- if (caps_len < NDIS_RSS_CAPS_SIZE_6_0) {
- if_printf(sc->hn_ifp, "invalid NDIS RSS caps len %zu",
- caps_len);
+
+ /*
+ * Preliminary verification.
+ */
+ if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) {
+ if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
+ caps.ndis_hdr.ndis_type);
+ return (EINVAL);
+ }
+ if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) {
+ if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
+ caps.ndis_hdr.ndis_rev);
+ return (EINVAL);
+ }
+ if (caps.ndis_hdr.ndis_size > caps_len) {
+ if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
+ "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len);
+ return (EINVAL);
+ } else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) {
+ if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
+ caps.ndis_hdr.ndis_size);
return (EINVAL);
}
@@ -746,7 +792,7 @@ hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt)
}
*rxr_cnt = caps.ndis_nrxr;
- if (caps_len == NDIS_RSS_CAPS_SIZE) {
+ if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE) {
if (bootverbose) {
if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
caps.ndis_nind);
@@ -806,12 +852,19 @@ done:
}
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;
+ uint32_t caps = 0;
size_t paramsz;
- int error;
+ int error, tso_maxsz, tso_minsg;
+
+ error = hn_rndis_query_hwcaps(sc, &hwcaps);
+ if (error) {
+ if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error);
+ return (error);
+ }
/* NOTE: 0 means "no change" */
memset(&params, 0, sizeof(params));
@@ -826,18 +879,136 @@ hn_rndis_conf_offload(struct hn_softc *sc)
}
params.ndis_hdr.ndis_size = paramsz;
- caps = HN_CAP_IPCS | HN_CAP_TCP4CS | HN_CAP_TCP6CS;
- params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
- params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
- params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
- if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
- caps |= HN_CAP_UDP4CS | HN_CAP_UDP6CS;
- params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
- params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
+ /*
+ * 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;
+
+ 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
+ }
+ 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 */
+ if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) ==
+ HN_NDIS_TXCSUM_CAP_IP4) {
+ caps |= HN_CAP_IPCS;
+ params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX;
+ }
+ if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4) {
+ if (params.ndis_ip4csum == NDIS_OFFLOAD_PARAM_TX)
+ params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
+ else
+ params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX;
+ }
+
+ /* TCP4 checksum */
+ if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4) ==
+ HN_NDIS_TXCSUM_CAP_TCP4) {
+ caps |= HN_CAP_TCP4CS;
+ params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX;
+ }
+ if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) {
+ if (params.ndis_tcp4csum == NDIS_OFFLOAD_PARAM_TX)
+ params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
+ else
+ params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX;
+ }
+
+ /* UDP4 checksum */
+ if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
+ caps |= HN_CAP_UDP4CS;
+ params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX;
+ }
+ if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) {
+ if (params.ndis_udp4csum == NDIS_OFFLOAD_PARAM_TX)
+ params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
+ else
+ params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX;
+ }
+
+ /* TCP6 checksum */
+ if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6) ==
+ HN_NDIS_TXCSUM_CAP_TCP6) {
+ caps |= HN_CAP_TCP6CS;
+ params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX;
+ }
+ if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6) {
+ if (params.ndis_tcp6csum == NDIS_OFFLOAD_PARAM_TX)
+ params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
+ else
+ params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX;
+ }
+
+ /* UDP6 checksum */
+ if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_UDP6) ==
+ HN_NDIS_TXCSUM_CAP_UDP6) {
+ caps |= HN_CAP_UDP6CS;
+ params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX;
+ }
+ if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6) {
+ if (params.ndis_udp6csum == NDIS_OFFLOAD_PARAM_TX)
+ params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
+ else
+ params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX;
+ }
+
+ if (bootverbose) {
+ if_printf(sc->hn_ifp, "offload csum: "
+ "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n",
+ params.ndis_ip4csum,
+ params.ndis_tcp4csum,
+ params.ndis_udp4csum,
+ params.ndis_tcp6csum,
+ params.ndis_udp6csum);
+ if_printf(sc->hn_ifp, "offload lsov2: ip4 %u, ip6 %u\n",
+ params.ndis_lsov2_ip4,
+ params.ndis_lsov2_ip6);
}
- caps |= HN_CAP_TSO4;
- params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
- /* XXX ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON */
error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, &params, paramsz);
if (error) {
@@ -994,8 +1165,92 @@ hn_rndis_halt(struct hn_softc *sc)
return (0);
}
+static int
+hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
+{
+ struct ndis_offload in;
+ size_t caps_len, size;
+ int error;
+
+ memset(&in, 0, sizeof(in));
+ in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD;
+ if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
+ in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3;
+ size = NDIS_OFFLOAD_SIZE;
+ } else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) {
+ in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2;
+ size = NDIS_OFFLOAD_SIZE_6_1;
+ } else {
+ in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
+ size = NDIS_OFFLOAD_SIZE_6_0;
+ }
+ in.ndis_hdr.ndis_size = size;
+
+ caps_len = NDIS_OFFLOAD_SIZE;
+ error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
+ &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0);
+ if (error)
+ return (error);
+
+ /*
+ * Preliminary verification.
+ */
+ if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) {
+ if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
+ caps->ndis_hdr.ndis_type);
+ return (EINVAL);
+ }
+ if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) {
+ if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
+ caps->ndis_hdr.ndis_rev);
+ return (EINVAL);
+ }
+ if (caps->ndis_hdr.ndis_size > caps_len) {
+ if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
+ "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len);
+ return (EINVAL);
+ } else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) {
+ if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
+ caps->ndis_hdr.ndis_size);
+ return (EINVAL);
+ }
+
+ if (bootverbose) {
+ /*
+ * NOTE:
+ * caps->ndis_hdr.ndis_size MUST be checked before accessing
+ * NDIS 6.1+ specific fields.
+ */
+ if_printf(sc->hn_ifp, "hwcaps rev %u\n",
+ caps->ndis_hdr.ndis_rev);
+
+ if_printf(sc->hn_ifp, "hwcaps csum: "
+ "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, "
+ "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n",
+ caps->ndis_csum.ndis_ip4_txcsum,
+ caps->ndis_csum.ndis_ip4_txenc,
+ caps->ndis_csum.ndis_ip4_rxcsum,
+ caps->ndis_csum.ndis_ip4_rxenc,
+ caps->ndis_csum.ndis_ip6_txcsum,
+ caps->ndis_csum.ndis_ip6_txenc,
+ caps->ndis_csum.ndis_ip6_rxcsum,
+ caps->ndis_csum.ndis_ip6_rxenc);
+ if_printf(sc->hn_ifp, "hwcaps lsov2: "
+ "ip4 maxsz %u minsg %u encap 0x%x, "
+ "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n",
+ caps->ndis_lsov2.ndis_ip4_maxsz,
+ caps->ndis_lsov2.ndis_ip4_minsg,
+ caps->ndis_lsov2.ndis_ip4_encap,
+ caps->ndis_lsov2.ndis_ip6_maxsz,
+ caps->ndis_lsov2.ndis_ip6_minsg,
+ caps->ndis_lsov2.ndis_ip6_encap,
+ caps->ndis_lsov2.ndis_ip6_opts);
+ }
+ return (0);
+}
+
int
-hn_rndis_attach(struct hn_softc *sc)
+hn_rndis_attach(struct hn_softc *sc, int mtu)
{
int error;
@@ -1010,7 +1265,7 @@ hn_rndis_attach(struct hn_softc *sc)
* Configure NDIS offload settings.
* XXX no offloading, if error happened?
*/
- hn_rndis_conf_offload(sc);
+ hn_rndis_conf_offload(sc, mtu);
return (0);
}
diff --git a/sys/dev/hyperv/netvsc/if_hnvar.h b/sys/dev/hyperv/netvsc/if_hnvar.h
index e48033c..2545f37 100644
--- a/sys/dev/hyperv/netvsc/if_hnvar.h
+++ b/sys/dev/hyperv/netvsc/if_hnvar.h
@@ -117,7 +117,7 @@ struct rndis_packet_msg;
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 *,
diff --git a/sys/dev/hyperv/netvsc/ndis.h b/sys/dev/hyperv/netvsc/ndis.h
index 28920f8..fed262d 100644
--- a/sys/dev/hyperv/netvsc/ndis.h
+++ b/sys/dev/hyperv/netvsc/ndis.h
@@ -59,6 +59,7 @@
#define NDIS_OBJTYPE_DEFAULT 0x80
#define NDIS_OBJTYPE_RSS_CAPS 0x88
#define NDIS_OBJTYPE_RSS_PARAMS 0x89
+#define NDIS_OBJTYPE_OFFLOAD 0xa7
struct ndis_object_hdr {
uint8_t ndis_type; /* NDIS_OBJTYPE_ */
@@ -205,6 +206,129 @@ struct ndis_rssprm_toeplitz {
};
/*
+ * OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES
+ * ndis_type: NDIS_OBJTYPE_OFFLOAD
+ */
+
+#define NDIS_OFFLOAD_ENCAP_NONE 0x0000
+#define NDIS_OFFLOAD_ENCAP_NULL 0x0001
+#define NDIS_OFFLOAD_ENCAP_8023 0x0002
+#define NDIS_OFFLOAD_ENCAP_8023PQ 0x0004
+#define NDIS_OFFLOAD_ENCAP_8023PQ_OOB 0x0008
+#define NDIS_OFFLOAD_ENCAP_RFC1483 0x0010
+
+struct ndis_csum_offload {
+ uint32_t ndis_ip4_txenc; /*NDIS_OFFLOAD_ENCAP_*/
+ uint32_t ndis_ip4_txcsum;
+#define NDIS_TXCSUM_CAP_IP4OPT 0x001
+#define NDIS_TXCSUM_CAP_TCP4OPT 0x004
+#define NDIS_TXCSUM_CAP_TCP4 0x010
+#define NDIS_TXCSUM_CAP_UDP4 0x040
+#define NDIS_TXCSUM_CAP_IP4 0x100
+ uint32_t ndis_ip4_rxenc; /*NDIS_OFFLOAD_ENCAP_*/
+ uint32_t ndis_ip4_rxcsum;
+#define NDIS_RXCSUM_CAP_IP4OPT 0x001
+#define NDIS_RXCSUM_CAP_TCP4OPT 0x004
+#define NDIS_RXCSUM_CAP_TCP4 0x010
+#define NDIS_RXCSUM_CAP_UDP4 0x040
+#define NDIS_RXCSUM_CAP_IP4 0x100
+ uint32_t ndis_ip6_txenc; /*NDIS_OFFLOAD_ENCAP_*/
+ uint32_t ndis_ip6_txcsum;
+#define NDIS_TXCSUM_CAP_IP6EXT 0x001
+#define NDIS_TXCSUM_CAP_TCP6OPT 0x004
+#define NDIS_TXCSUM_CAP_TCP6 0x010
+#define NDIS_TXCSUM_CAP_UDP6 0x040
+ uint32_t ndis_ip6_rxenc; /*NDIS_OFFLOAD_ENCAP_*/
+ uint32_t ndis_ip6_rxcsum;
+#define NDIS_RXCSUM_CAP_IP6EXT 0x001
+#define NDIS_RXCSUM_CAP_TCP6OPT 0x004
+#define NDIS_RXCSUM_CAP_TCP6 0x010
+#define NDIS_RXCSUM_CAP_UDP6 0x040
+};
+
+struct ndis_lsov1_offload {
+ uint32_t ndis_encap; /*NDIS_OFFLOAD_ENCAP_*/
+ uint32_t ndis_maxsize;
+ uint32_t ndis_minsegs;
+ uint32_t ndis_opts;
+};
+
+struct ndis_ipsecv1_offload {
+ uint32_t ndis_encap; /*NDIS_OFFLOAD_ENCAP_*/
+ uint32_t ndis_ah_esp;
+ uint32_t ndis_xport_tun;
+ uint32_t ndis_ip4_opts;
+ uint32_t ndis_flags;
+ uint32_t ndis_ip4_ah;
+ uint32_t ndis_ip4_esp;
+};
+
+struct ndis_lsov2_offload {
+ uint32_t ndis_ip4_encap; /*NDIS_OFFLOAD_ENCAP_*/
+ uint32_t ndis_ip4_maxsz;
+ uint32_t ndis_ip4_minsg;
+ uint32_t ndis_ip6_encap; /*NDIS_OFFLOAD_ENCAP_*/
+ uint32_t ndis_ip6_maxsz;
+ uint32_t ndis_ip6_minsg;
+ uint32_t ndis_ip6_opts;
+#define NDIS_LSOV2_CAP_IP6EXT 0x001
+#define NDIS_LSOV2_CAP_TCP6OPT 0x004
+};
+
+struct ndis_ipsecv2_offload {
+ uint32_t ndis_encap; /*NDIS_OFFLOAD_ENCAP_*/
+ uint16_t ndis_ip6;
+ uint16_t ndis_ip4opt;
+ uint16_t ndis_ip6ext;
+ uint16_t ndis_ah;
+ uint16_t ndis_esp;
+ uint16_t ndis_ah_esp;
+ uint16_t ndis_xport;
+ uint16_t ndis_tun;
+ uint16_t ndis_xport_tun;
+ uint16_t ndis_lso;
+ uint16_t ndis_extseq;
+ uint32_t ndis_udp_esp;
+ uint32_t ndis_auth;
+ uint32_t ndis_crypto;
+ uint32_t ndis_sa_caps;
+};
+
+struct ndis_rsc_offload {
+ uint16_t ndis_ip4;
+ uint16_t ndis_ip6;
+};
+
+struct ndis_encap_offload {
+ uint32_t ndis_flags;
+ uint32_t ndis_maxhdr;
+};
+
+struct ndis_offload {
+ struct ndis_object_hdr ndis_hdr;
+ struct ndis_csum_offload ndis_csum;
+ struct ndis_lsov1_offload ndis_lsov1;
+ struct ndis_ipsecv1_offload ndis_ipsecv1;
+ struct ndis_lsov2_offload ndis_lsov2;
+ uint32_t ndis_flags;
+ /* NDIS >= 6.1 */
+ struct ndis_ipsecv2_offload ndis_ipsecv2;
+ /* NDIS >= 6.30 */
+ struct ndis_rsc_offload ndis_rsc;
+ struct ndis_encap_offload ndis_encap_gre;
+};
+
+#define NDIS_OFFLOAD_SIZE sizeof(struct ndis_offload)
+#define NDIS_OFFLOAD_SIZE_6_0 \
+ __offsetof(struct ndis_offload, ndis_ipsecv2)
+#define NDIS_OFFLOAD_SIZE_6_1 \
+ __offsetof(struct ndis_offload, ndis_rsc)
+
+#define NDIS_OFFLOAD_REV_1 1 /* NDIS 6.0 */
+#define NDIS_OFFLOAD_REV_2 2 /* NDIS 6.1 */
+#define NDIS_OFFLOAD_REV_3 3 /* NDIS 6.30 */
+
+/*
* Per-packet-info
*/
diff --git a/sys/net/rndis.h b/sys/net/rndis.h
index 2d7a5bf..9da76bc 100644
--- a/sys/net/rndis.h
+++ b/sys/net/rndis.h
@@ -87,6 +87,7 @@
#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
#define OID_TCP_OFFLOAD_PARAMETERS 0xFC01020C
+#define OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020D
#define RNDIS_MEDIUM_802_3 0x00000000
OpenPOWER on IntegriCloud