Author: sephe
Date: Fri Oct 13 05:09:56 2017
New Revision: 324578
URL: https://svnweb.freebsd.org/changeset/base/324578

Log:
  MFC 324489,324516
  
  324489
      hyperv/hn: Workaround erroneous hash type observed on WS2016.
  
      Background:
      - UDP 4-tuple hash type is unconditionally enabled in Hyper-V on WS2016,
        which is _not_ affected by NDIS_OBJTYPE_RSS_PARAMS.
      - Non-fragment UDP/IPv4 datagrams' hash type is delivered to VM as
        TCP_IPV4.
  
      Currently this erroneous behavior only applies to WS2016/Windows10.
  
      Force l3/l4 protocol check, if the RXed packet's hash type is TCP_IPV4,
      and the Hyper-V is running on WS2016/Windows10.  If the RXed packet is
      UDP datagram, adjust mbuf hash type to UDP_IPV4.
  
      Sponsored by:   Microsoft
  
  324516
      hyperv/hn: Workaround erroneous hash type observed on WS2016 for VF.
  
      The background was described in r324489.
  
      Sponsored by:   Microsoft

Modified:
  stable/11/sys/dev/hyperv/netvsc/hn_nvs.c
  stable/11/sys/dev/hyperv/netvsc/hn_rndis.c
  stable/11/sys/dev/hyperv/netvsc/if_hn.c
  stable/11/sys/dev/hyperv/netvsc/if_hnvar.h
  stable/11/sys/dev/hyperv/netvsc/ndis.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/hyperv/netvsc/hn_nvs.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hn_nvs.c    Fri Oct 13 05:02:39 2017        
(r324577)
+++ stable/11/sys/dev/hyperv/netvsc/hn_nvs.c    Fri Oct 13 05:09:56 2017        
(r324578)
@@ -601,6 +601,11 @@ hn_nvs_attach(struct hn_softc *sc, int mtu)
 {
        int error;
 
+       if (hyperv_ver_major >= 10) {
+               /* UDP 4-tuple hash is enforced. */
+               sc->hn_caps |= HN_CAP_UDPHASH;
+       }
+
        /*
         * Initialize NVS.
         */

Modified: stable/11/sys/dev/hyperv/netvsc/hn_rndis.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hn_rndis.c  Fri Oct 13 05:02:39 2017        
(r324577)
+++ stable/11/sys/dev/hyperv/netvsc/hn_rndis.c  Fri Oct 13 05:09:56 2017        
(r324578)
@@ -521,6 +521,10 @@ hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_c
        /* Commit! */
        sc->hn_rss_ind_size = indsz;
        sc->hn_rss_hcap = hash_func | hash_types;
+       if (sc->hn_caps & HN_CAP_UDPHASH) {
+               /* UDP 4-tuple hash is unconditionally enabled. */
+               sc->hn_rss_hcap |= NDIS_HASH_UDP_IPV4_X;
+       }
        *rxr_cnt0 = rxr_cnt;
        return (0);
 }
@@ -760,8 +764,10 @@ hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags)
            ("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_hash & NDIS_HASH_FUNCTION_MASK),
+           ("no hash func %08x", sc->hn_rss_hash));
+       KASSERT((sc->hn_rss_hash & NDIS_HASH_STD),
+           ("no standard hash types %08x", sc->hn_rss_hash));
        KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size"));
 
        if (bootverbose) {
@@ -780,7 +786,8 @@ hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags)
        prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
        prm->ndis_hdr.ndis_size = rss_size;
        prm->ndis_flags = flags;
-       prm->ndis_hash = sc->hn_rss_hash;
+       prm->ndis_hash = sc->hn_rss_hash &
+           (NDIS_HASH_FUNCTION_MASK | NDIS_HASH_STD);
        prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size;
        prm->ndis_indoffset =
            __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]);

Modified: stable/11/sys/dev/hyperv/netvsc/if_hn.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hn.c     Fri Oct 13 05:02:39 2017        
(r324577)
+++ stable/11/sys/dev/hyperv/netvsc/if_hn.c     Fri Oct 13 05:09:56 2017        
(r324578)
@@ -385,6 +385,7 @@ static void                 hn_link_status(struct hn_softc 
*);
 static int                     hn_create_rx_data(struct hn_softc *, int);
 static void                    hn_destroy_rx_data(struct hn_softc *);
 static int                     hn_check_iplen(const struct mbuf *, int);
+static void                    hn_rxpkt_proto(const struct mbuf *, int *, int 
*);
 static int                     hn_set_rxfilter(struct hn_softc *, uint32_t);
 static int                     hn_rxfilter_config(struct hn_softc *);
 static int                     hn_rss_reconfig(struct hn_softc *);
@@ -399,6 +400,7 @@ static int                  hn_tx_ring_create(struct 
hn_softc *, int)
 static void                    hn_tx_ring_destroy(struct hn_tx_ring *);
 static int                     hn_create_tx_data(struct hn_softc *, int);
 static void                    hn_fixup_tx_data(struct hn_softc *);
+static void                    hn_fixup_rx_data(struct hn_softc *);
 static void                    hn_destroy_tx_data(struct hn_softc *);
 static void                    hn_txdesc_dmamap_destroy(struct hn_txdesc *);
 static void                    hn_txdesc_gc(struct hn_tx_ring *,
@@ -1422,6 +1424,8 @@ hn_rss_type_fromndis(uint32_t rss_hash)
                types |= RSS_TYPE_TCP_IPV6;
        if (rss_hash & NDIS_HASH_TCP_IPV6_EX)
                types |= RSS_TYPE_TCP_IPV6_EX;
+       if (rss_hash & NDIS_HASH_UDP_IPV4_X)
+               types |= RSS_TYPE_UDP_IPV4;
        return (types);
 }
 
@@ -1430,9 +1434,8 @@ hn_rss_type_tondis(uint32_t types)
 {
        uint32_t rss_hash = 0;
 
-       KASSERT((types &
-       (RSS_TYPE_UDP_IPV4 | RSS_TYPE_UDP_IPV6 | RSS_TYPE_UDP_IPV6_EX)) == 0,
-       ("UDP4, UDP6 and UDP6EX are not supported"));
+       KASSERT((types & (RSS_TYPE_UDP_IPV6 | RSS_TYPE_UDP_IPV6_EX)) == 0,
+           ("UDP6 and UDP6EX are not supported"));
 
        if (types & RSS_TYPE_IPV4)
                rss_hash |= NDIS_HASH_IPV4;
@@ -1446,6 +1449,8 @@ hn_rss_type_tondis(uint32_t types)
                rss_hash |= NDIS_HASH_TCP_IPV6;
        if (types & RSS_TYPE_TCP_IPV6_EX)
                rss_hash |= NDIS_HASH_TCP_IPV6_EX;
+       if (types & RSS_TYPE_UDP_IPV4)
+               rss_hash |= NDIS_HASH_UDP_IPV4_X;
        return (rss_hash);
 }
 
@@ -1544,6 +1549,13 @@ hn_vf_rss_fixup(struct hn_softc *sc, bool reconf)
         * NOTE:
         * We don't disable the hash type, but stop delivery the hash
         * value/type through mbufs on RX path.
+        *
+        * XXX If HN_CAP_UDPHASH is set in hn_caps, then UDP 4-tuple
+        * hash is delivered with type of TCP_IPV4.  This means if
+        * UDP_IPV4 is enabled, then TCP_IPV4 should be forced, at
+        * least to hn_mbuf_hash.  However, given that _all_ of the
+        * NICs implement TCP_IPV4, this will _not_ impose any issues
+        * here.
         */
        if ((my_types & RSS_TYPE_IPV4) &&
            (diff_types & ifrh.ifrh_types &
@@ -2238,9 +2250,10 @@ hn_attach(device_t dev)
 #endif
 
        /*
-        * Fixup TX stuffs after synthetic parts are attached.
+        * Fixup TX/RX stuffs after synthetic parts are attached.
         */
        hn_fixup_tx_data(sc);
+       hn_fixup_rx_data(sc);
 
        ctx = device_get_sysctl_ctx(dev);
        child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
@@ -3378,6 +3391,7 @@ hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int
        struct mbuf *m_new;
        int size, do_lro = 0, do_csum = 1, is_vf = 0;
        int hash_type = M_HASHTYPE_NONE;
+       int l3proto = ETHERTYPE_MAX, l4proto = IPPROTO_DONE;
 
        ifp = hn_ifp;
        if (rxr->hn_rxvf_ifp != NULL) {
@@ -3477,31 +3491,9 @@ hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int
                    (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK))
                        do_lro = 1;
        } else {
-               const struct ether_header *eh;
-               uint16_t etype;
-               int hoff;
-
-               hoff = sizeof(*eh);
-               /* Checked at the beginning of this function. */
-               KASSERT(m_new->m_len >= hoff, ("not ethernet frame"));
-
-               eh = mtod(m_new, struct ether_header *);
-               etype = ntohs(eh->ether_type);
-               if (etype == ETHERTYPE_VLAN) {
-                       const struct ether_vlan_header *evl;
-
-                       hoff = sizeof(*evl);
-                       if (m_new->m_len < hoff)
-                               goto skip;
-                       evl = mtod(m_new, struct ether_vlan_header *);
-                       etype = ntohs(evl->evl_proto);
-               }
-
-               if (etype == ETHERTYPE_IP) {
-                       int pr;
-
-                       pr = hn_check_iplen(m_new, hoff);
-                       if (pr == IPPROTO_TCP) {
+               hn_rxpkt_proto(m_new, &l3proto, &l4proto);
+               if (l3proto == ETHERTYPE_IP) {
+                       if (l4proto == IPPROTO_TCP) {
                                if (do_csum &&
                                    (rxr->hn_trust_hcsum &
                                     HN_TRUST_HCSUM_TCP)) {
@@ -3512,7 +3504,7 @@ hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int
                                        m_new->m_pkthdr.csum_data = 0xffff;
                                }
                                do_lro = 1;
-                       } else if (pr == IPPROTO_UDP) {
+                       } else if (l4proto == IPPROTO_UDP) {
                                if (do_csum &&
                                    (rxr->hn_trust_hcsum &
                                     HN_TRUST_HCSUM_UDP)) {
@@ -3522,7 +3514,7 @@ hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int
                                            CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
                                        m_new->m_pkthdr.csum_data = 0xffff;
                                }
-                       } else if (pr != IPPROTO_DONE && do_csum &&
+                       } else if (l4proto != IPPROTO_DONE && do_csum &&
                            (rxr->hn_trust_hcsum & HN_TRUST_HCSUM_IP)) {
                                rxr->hn_csum_trusted++;
                                m_new->m_pkthdr.csum_flags |=
@@ -3530,7 +3522,7 @@ hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int
                        }
                }
        }
-skip:
+
        if (info->vlan_info != HN_NDIS_VLAN_INFO_INVALID) {
                m_new->m_pkthdr.ether_vtag = EVL_MAKETAG(
                    NDIS_VLAN_INFO_ID(info->vlan_info),
@@ -3585,6 +3577,37 @@ skip:
 
                        case NDIS_HASH_TCP_IPV4:
                                hash_type = M_HASHTYPE_RSS_TCP_IPV4;
+                               if (rxr->hn_rx_flags & HN_RX_FLAG_UDP_HASH) {
+                                       int def_htype = M_HASHTYPE_OPAQUE_HASH;
+
+                                       if (is_vf)
+                                               def_htype = M_HASHTYPE_NONE;
+
+                                       /*
+                                        * UDP 4-tuple hash is delivered as
+                                        * TCP 4-tuple hash.
+                                        */
+                                       if (l3proto == ETHERTYPE_MAX) {
+                                               hn_rxpkt_proto(m_new,
+                                                   &l3proto, &l4proto);
+                                       }
+                                       if (l3proto == ETHERTYPE_IP) {
+                                               if (l4proto == IPPROTO_UDP &&
+                                                   (rxr->hn_mbuf_hash &
+                                                    NDIS_HASH_UDP_IPV4_X)) {
+                                                       hash_type =
+                                                       M_HASHTYPE_RSS_UDP_IPV4;
+                                                       do_lro = 0;
+                                               } else if (l4proto !=
+                                                   IPPROTO_TCP) {
+                                                       hash_type = def_htype;
+                                                       do_lro = 0;
+                                               }
+                                       } else {
+                                               hash_type = def_htype;
+                                               do_lro = 0;
+                                       }
+                               }
                                break;
 
                        case NDIS_HASH_IPV6:
@@ -4835,6 +4858,36 @@ hn_check_iplen(const struct mbuf *m, int hoff)
        return ip->ip_p;
 }
 
+static void
+hn_rxpkt_proto(const struct mbuf *m_new, int *l3proto, int *l4proto)
+{
+       const struct ether_header *eh;
+       uint16_t etype;
+       int hoff;
+
+       hoff = sizeof(*eh);
+       /* Checked at the beginning of this function. */
+       KASSERT(m_new->m_len >= hoff, ("not ethernet frame"));
+
+       eh = mtod(m_new, const struct ether_header *);
+       etype = ntohs(eh->ether_type);
+       if (etype == ETHERTYPE_VLAN) {
+               const struct ether_vlan_header *evl;
+
+               hoff = sizeof(*evl);
+               if (m_new->m_len < hoff)
+                       return;
+               evl = mtod(m_new, const struct ether_vlan_header *);
+               etype = ntohs(evl->evl_proto);
+       }
+       *l3proto = etype;
+
+       if (etype == ETHERTYPE_IP)
+               *l4proto = hn_check_iplen(m_new, hoff);
+       else
+               *l4proto = IPPROTO_DONE;
+}
+
 static int
 hn_create_rx_data(struct hn_softc *sc, int ring_cnt)
 {
@@ -5543,6 +5596,18 @@ hn_fixup_tx_data(struct hn_softc *sc)
                        if_printf(sc->hn_ifp, "support HASHVAL pktinfo\n");
                for (i = 0; i < sc->hn_tx_ring_cnt; ++i)
                        sc->hn_tx_ring[i].hn_tx_flags |= HN_TX_FLAG_HASHVAL;
+       }
+}
+
+static void
+hn_fixup_rx_data(struct hn_softc *sc)
+{
+
+       if (sc->hn_caps & HN_CAP_UDPHASH) {
+               int i;
+
+               for (i = 0; i < sc->hn_rx_ring_cnt; ++i)
+                       sc->hn_rx_ring[i].hn_rx_flags |= HN_RX_FLAG_UDP_HASH;
        }
 }
 

Modified: stable/11/sys/dev/hyperv/netvsc/if_hnvar.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hnvar.h  Fri Oct 13 05:02:39 2017        
(r324577)
+++ stable/11/sys/dev/hyperv/netvsc/if_hnvar.h  Fri Oct 13 05:09:56 2017        
(r324578)
@@ -97,6 +97,7 @@ struct hn_rx_ring {
 #define HN_RX_FLAG_ATTACHED    0x0001
 #define HN_RX_FLAG_BR_REF      0x0002
 #define HN_RX_FLAG_XPNT_VF     0x0004
+#define HN_RX_FLAG_UDP_HASH    0x0008
 
 struct hn_tx_ring {
 #ifndef HN_USE_TXDESC_BUFRING
@@ -304,11 +305,12 @@ do {                                              \
 #define HN_CAP_TSO4                    0x0080
 #define HN_CAP_TSO6                    0x0100
 #define HN_CAP_HASHVAL                 0x0200
+#define HN_CAP_UDPHASH                 0x0400
 
 /* Capability description for use with printf(9) %b identifier. */
 #define HN_CAP_BITS                            \
        "\020\1VLAN\2MTU\3IPCS\4TCP4CS\5TCP6CS" \
-       "\6UDP4CS\7UDP6CS\10TSO4\11TSO6\12HASHVAL"
+       "\6UDP4CS\7UDP6CS\10TSO4\11TSO6\12HASHVAL\13UDPHASH"
 
 #define HN_LINK_FLAG_LINKUP            0x0001
 #define HN_LINK_FLAG_NETCHG            0x0002

Modified: stable/11/sys/dev/hyperv/netvsc/ndis.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/ndis.h      Fri Oct 13 05:02:39 2017        
(r324577)
+++ stable/11/sys/dev/hyperv/netvsc/ndis.h      Fri Oct 13 05:09:56 2017        
(r324578)
@@ -56,17 +56,26 @@
 #define        NDIS_HASH_IPV6_EX               0x00000800
 #define        NDIS_HASH_TCP_IPV6              0x00001000
 #define        NDIS_HASH_TCP_IPV6_EX           0x00002000
+#define        NDIS_HASH_UDP_IPV4_X            0x00004000      /* XXX 
non-standard */
 
 #define        NDIS_HASH_ALL                   (NDIS_HASH_IPV4 |       \
                                         NDIS_HASH_TCP_IPV4 |   \
                                         NDIS_HASH_IPV6 |       \
                                         NDIS_HASH_IPV6_EX |    \
                                         NDIS_HASH_TCP_IPV6 |   \
+                                        NDIS_HASH_TCP_IPV6_EX |\
+                                        NDIS_HASH_UDP_IPV4_X)
+
+#define        NDIS_HASH_STD                   (NDIS_HASH_IPV4 |       \
+                                        NDIS_HASH_TCP_IPV4 |   \
+                                        NDIS_HASH_IPV6 |       \
+                                        NDIS_HASH_IPV6_EX |    \
+                                        NDIS_HASH_TCP_IPV6 |   \
                                         NDIS_HASH_TCP_IPV6_EX)
 
 /* Hash description for use with printf(9) %b identifier. */
 #define        NDIS_HASH_BITS                  \
-       "\20\1TOEPLITZ\11IP4\12TCP4\13IP6\14IP6EX\15TCP6\16TCP6EX"
+       "\20\1TOEPLITZ\11IP4\12TCP4\13IP6\14IP6EX\15TCP6\16TCP6EX\17UDP4_X"
 
 #define        NDIS_HASH_KEYSIZE_TOEPLITZ      40
 #define        NDIS_HASH_INDCNT                128
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to