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 @@ -255,6 +255,8 @@ int hn_ndis_tso_szmax; int hn_ndis_tso_sgmin; + int hn_rss_ind_size; + uint32_t hn_rss_hash; /* NDIS_HASH_ */ 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 @@ -329,6 +329,7 @@ static int hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS); static int hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS); static int hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS); +static int hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS); static int hn_check_iplen(const struct mbuf *, int); static int hn_create_tx_ring(struct hn_softc *, int); static void hn_destroy_tx_ring(struct hn_tx_ring *); @@ -770,6 +771,11 @@ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, hn_rxfilter_sysctl, "A", "rxfilter"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hash", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, + hn_rss_hash_sysctl, "A", "RSS hash"); + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rss_ind_size", + CTLFLAG_RD, &sc->hn_rss_ind_size, 0, "RSS indirect entry count"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key", CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0, hn_rss_key_sysctl, "IU", "RSS key"); @@ -2479,6 +2485,20 @@ } static int +hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct hn_softc *sc = arg1; + char hash_str[128]; + uint32_t hash; + + HN_LOCK(sc); + hash = sc->hn_rss_hash; + HN_UNLOCK(sc); + snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS); + return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req); +} + +static int hn_check_iplen(const struct mbuf *m, int hoff) { const struct ip *ip; @@ -3642,6 +3662,10 @@ old_caps = sc->hn_caps; sc->hn_caps = 0; + /* Clear RSS stuffs. */ + sc->hn_rss_ind_size = 0; + sc->hn_rss_hash = 0; + /* * Attach the primary channel _before_ attaching NVS and RNDIS. */ @@ -3716,7 +3740,6 @@ if_printf(sc->hn_ifp, "setup default RSS indirect " "table\n"); } - /* TODO: Take ndis_rss_caps.ndis_nind into account. */ for (i = 0; i < NDIS_HASH_INDCNT; ++i) rss->rss_ind[i] = i % nchan; sc->hn_flags |= HN_FLAG_HAS_RSSIND; 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 @@ -747,13 +747,14 @@ } int -hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt) +hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0) { struct ndis_rss_caps in, caps; size_t caps_len; - int error; + int error, indsz, rxr_cnt, hash_fnidx; + uint32_t hash_func = 0, hash_types = 0; - *rxr_cnt = 0; + *rxr_cnt0 = 0; if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20) return (EOPNOTSUPP); @@ -792,18 +793,73 @@ return (EINVAL); } + /* + * Save information for later RSS configuration. + */ if (caps.ndis_nrxr == 0) { if_printf(sc->hn_ifp, "0 RX rings!?\n"); return (EINVAL); } - *rxr_cnt = caps.ndis_nrxr; + if (bootverbose) + if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr); + rxr_cnt = caps.ndis_nrxr; + + if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE && + caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) { + if (caps.ndis_nind > NDIS_HASH_INDCNT) { + if_printf(sc->hn_ifp, + "too many RSS indirect table entries %u\n", + caps.ndis_nind); + return (EOPNOTSUPP); + } + if (!powerof2(caps.ndis_nind)) { + if_printf(sc->hn_ifp, "RSS indirect table size is not " + "power-of-2 %u\n", caps.ndis_nind); + } - 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); } + indsz = caps.ndis_nind; + } else { + indsz = NDIS_HASH_INDCNT; + } + if (indsz < rxr_cnt) { + if_printf(sc->hn_ifp, "# of RX rings (%d) > " + "RSS indirect table size %d\n", rxr_cnt, indsz); + rxr_cnt = indsz; } + + /* + * NOTE: + * Toeplitz is at the lowest bit, and it is prefered; so ffs(), + * instead of fls(), is used here. + */ + hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK); + if (hash_fnidx == 0) { + if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n", + caps.ndis_caps); + return (EOPNOTSUPP); + } + hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */ + + if (caps.ndis_caps & NDIS_RSS_CAP_IPV4) + hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4; + if (caps.ndis_caps & NDIS_RSS_CAP_IPV6) + hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6; + if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX) + hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX; + if (hash_types == 0) { + if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n", + caps.ndis_caps); + return (EOPNOTSUPP); + } + + /* Commit! */ + sc->hn_rss_ind_size = indsz; + sc->hn_rss_hash = hash_func | hash_types; + *rxr_cnt0 = rxr_cnt; return (0); } @@ -1033,7 +1089,7 @@ { struct ndis_rssprm_toeplitz *rss = &sc->hn_rss; struct ndis_rss_params *prm = &rss->rss_params; - int error; + int error, rss_size; /* * Only NDIS 6.20+ is supported: @@ -1043,21 +1099,29 @@ KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_20, ("NDIS 6.20+ is required, NDIS version 0x%08x", sc->hn_ndis_ver)); + /* XXX only one can be specified through, popcnt? */ + KASSERT((sc->hn_rss_hash & NDIS_HASH_FUNCTION_MASK), ("no hash func")); + KASSERT((sc->hn_rss_hash & NDIS_HASH_TYPE_MASK), ("no hash types")); + KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size")); + + if (bootverbose) { + if_printf(sc->hn_ifp, "RSS indirect table size %d, " + "hash 0x%08x\n", sc->hn_rss_ind_size, sc->hn_rss_hash); + } + /* * NOTE: * DO NOT whack rss_key and rss_ind, which are setup by the caller. */ memset(prm, 0, sizeof(*prm)); + rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size); prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS; prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2; - prm->ndis_hdr.ndis_size = sizeof(*rss); + prm->ndis_hdr.ndis_size = rss_size; prm->ndis_flags = flags; - prm->ndis_hash = NDIS_HASH_FUNCTION_TOEPLITZ | - NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4 | - NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6; - /* TODO: Take ndis_rss_caps.ndis_nind into account */ - prm->ndis_indsize = sizeof(rss->rss_ind); + prm->ndis_hash = sc->hn_rss_hash; + prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size; prm->ndis_indoffset = __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]); prm->ndis_keysize = sizeof(rss->rss_key); @@ -1065,7 +1129,7 @@ __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]); error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS, - rss, sizeof(*rss)); + rss, rss_size); if (error) { if_printf(sc->hn_ifp, "RSS config failed: %d\n", error); } else { Index: head/sys/dev/hyperv/netvsc/ndis.h =================================================================== --- head/sys/dev/hyperv/netvsc/ndis.h +++ head/sys/dev/hyperv/netvsc/ndis.h @@ -57,6 +57,10 @@ #define NDIS_HASH_TCP_IPV6 0x00001000 #define NDIS_HASH_TCP_IPV6_EX 0x00002000 +/* Hash description for use with printf(9) %b identifier. */ +#define NDIS_HASH_BITS \ + "\20\1TOEPLITZ\11IP4\12TCP4\13IP6\14IP6EX\15TCP6\16TCP6EX" + #define NDIS_HASH_KEYSIZE_TOEPLITZ 40 #define NDIS_HASH_INDCNT 128 @@ -142,7 +146,7 @@ */ struct ndis_rss_caps { struct ndis_object_hdr ndis_hdr; - uint32_t ndis_flags; /* NDIS_RSS_CAP_ */ + uint32_t ndis_caps; /* NDIS_RSS_CAP_ */ uint32_t ndis_nmsi; /* # of MSIs */ uint32_t ndis_nrxr; /* # of RX rings */ /* NDIS >= 6.30 */ @@ -165,7 +169,8 @@ #define NDIS_RSS_CAP_IPV4 0x00000100 #define NDIS_RSS_CAP_IPV6 0x00000200 #define NDIS_RSS_CAP_IPV6_EX 0x00000400 -#define NDIS_RSS_CAP_HASH_TOEPLITZ 0x00000001 +#define NDIS_RSS_CAP_HASH_TOEPLITZ NDIS_HASH_FUNCTION_TOEPLITZ +#define NDIS_RSS_CAP_HASHFUNC_MASK NDIS_HASH_FUNCTION_MASK /* * OID_GEN_RECEIVE_SCALE_PARAMETERS @@ -209,6 +214,9 @@ uint32_t rss_ind[NDIS_HASH_INDCNT]; }; +#define NDIS_RSSPRM_TOEPLITZ_SIZE(nind) \ + __offsetof(struct ndis_rssprm_toeplitz, rss_ind[nind]) + /* * OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES * ndis_type: NDIS_OBJTYPE_OFFLOAD