When multiple threads issue RNDIS command requests (such as device
info queries) simultaneously, command failures can occur due to
concurrent access to shared resources in the RNDIS execution path.
The RNDIS layer only supports one pending request at a time, and
hn_rndis_exec1() returns -EBUSY if a request is already in progress.

Functions like hn_rndis_get_offload(), hn_rndis_conf_offload(), and
hn_rndis_get_ptypes() were querying hardware capabilities on each
call, which could race with other RNDIS commands from different
threads.

Fix this by querying the hardware offload capabilities once during
device attach in hn_rndis_attach() and caching the result in the
hn_data structure. All subsequent accesses use the cached value,
eliminating the race condition.

If the hwcaps query fails during attach, the device initialization
fails entirely. This matches the behavior of the Linux netvsc driver
(see rndis_netdev_set_hwcaps in rndis_filter.c).

Fixes: 4e9c73e96e83 ("net/netvsc: add Hyper-V network device")
Cc: [email protected]

Reported-by: Madhuker Mythri <[email protected]>
Signed-off-by: Stephen Hemminger <[email protected]>
---
 drivers/net/netvsc/hn_rndis.c | 110 ++++++++++++++++------------------
 drivers/net/netvsc/hn_var.h   |   4 ++
 2 files changed, 57 insertions(+), 57 deletions(-)

diff --git a/drivers/net/netvsc/hn_rndis.c b/drivers/net/netvsc/hn_rndis.c
index 7c54eebcef..ed053ee905 100644
--- a/drivers/net/netvsc/hn_rndis.c
+++ b/drivers/net/netvsc/hn_rndis.c
@@ -781,15 +781,9 @@ int hn_rndis_conf_offload(struct hn_data *hv,
                          uint64_t tx_offloads, uint64_t rx_offloads)
 {
        struct ndis_offload_params params;
-       struct ndis_offload hwcaps;
+       const struct ndis_offload *hwcaps = &hv->hwcaps;
        int error;
 
-       error = hn_rndis_query_hwcaps(hv, &hwcaps);
-       if (error) {
-               PMD_DRV_LOG(ERR, "hwcaps query failed: %d", error);
-               return error;
-       }
-
        /* NOTE: 0 means "no change" */
        memset(&params, 0, sizeof(params));
 
@@ -803,25 +797,25 @@ int hn_rndis_conf_offload(struct hn_data *hv,
        }
 
        if (tx_offloads & RTE_ETH_TX_OFFLOAD_TCP_CKSUM) {
-               if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_TCP4)
+               if (hwcaps->ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_TCP4)
                        params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX;
                else
                        goto unsupported;
 
-               if (hwcaps.ndis_csum.ndis_ip6_txcsum & NDIS_TXCSUM_CAP_TCP6)
+               if (hwcaps->ndis_csum.ndis_ip6_txcsum & NDIS_TXCSUM_CAP_TCP6)
                        params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX;
                else
                        goto unsupported;
        }
 
        if (rx_offloads & RTE_ETH_RX_OFFLOAD_TCP_CKSUM) {
-               if ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4)
+               if ((hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4)
                    == NDIS_RXCSUM_CAP_TCP4)
                        params.ndis_tcp4csum |= NDIS_OFFLOAD_PARAM_RX;
                else
                        goto unsupported;
 
-               if ((hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6)
+               if ((hwcaps->ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6)
                    == NDIS_RXCSUM_CAP_TCP6)
                        params.ndis_tcp6csum |= NDIS_OFFLOAD_PARAM_RX;
                else
@@ -829,12 +823,12 @@ int hn_rndis_conf_offload(struct hn_data *hv,
        }
 
        if (tx_offloads & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) {
-               if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4)
+               if (hwcaps->ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4)
                        params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX;
                else
                        goto unsupported;
 
-               if ((hwcaps.ndis_csum.ndis_ip6_txcsum & NDIS_TXCSUM_CAP_UDP6)
+               if ((hwcaps->ndis_csum.ndis_ip6_txcsum & NDIS_TXCSUM_CAP_UDP6)
                    == NDIS_TXCSUM_CAP_UDP6)
                        params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX;
                else
@@ -842,38 +836,38 @@ int hn_rndis_conf_offload(struct hn_data *hv,
        }
 
        if (rx_offloads & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) {
-               if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4)
+               if (hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4)
                        params.ndis_udp4csum |= NDIS_OFFLOAD_PARAM_RX;
                else
                        goto unsupported;
 
-               if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6)
+               if (hwcaps->ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6)
                        params.ndis_udp6csum |= NDIS_OFFLOAD_PARAM_RX;
                else
                        goto unsupported;
        }
 
        if (tx_offloads & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) {
-               if ((hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_IP4)
+               if ((hwcaps->ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_IP4)
                    == NDIS_TXCSUM_CAP_IP4)
                        params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX;
                else
                        goto unsupported;
        }
        if (rx_offloads & RTE_ETH_RX_OFFLOAD_IPV4_CKSUM) {
-               if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)
+               if (hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)
                        params.ndis_ip4csum |= NDIS_OFFLOAD_PARAM_RX;
                else
                        goto unsupported;
        }
 
        if (tx_offloads & RTE_ETH_TX_OFFLOAD_TCP_TSO) {
-               if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023)
+               if (hwcaps->ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023)
                        params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
                else
                        goto unsupported;
 
-               if ((hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6)
+               if ((hwcaps->ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6)
                    == HN_NDIS_LSOV2_CAP_IP6)
                        params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON;
                else
@@ -898,51 +892,42 @@ int hn_rndis_conf_offload(struct hn_data *hv,
 int hn_rndis_get_offload(struct hn_data *hv,
                         struct rte_eth_dev_info *dev_info)
 {
-       struct ndis_offload hwcaps;
-       int error;
-
-       memset(&hwcaps, 0, sizeof(hwcaps));
-
-       error = hn_rndis_query_hwcaps(hv, &hwcaps);
-       if (error) {
-               PMD_DRV_LOG(ERR, "hwcaps query failed: %d", error);
-               return error;
-       }
+       const struct ndis_offload *hwcaps = &hv->hwcaps;
 
        dev_info->tx_offload_capa = RTE_ETH_TX_OFFLOAD_MULTI_SEGS |
                                    RTE_ETH_TX_OFFLOAD_VLAN_INSERT;
 
-       if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4)
+       if ((hwcaps->ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4)
            == HN_NDIS_TXCSUM_CAP_IP4)
                dev_info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;
 
-       if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4)
+       if ((hwcaps->ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4)
            == HN_NDIS_TXCSUM_CAP_TCP4 &&
-           (hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6)
+           (hwcaps->ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6)
            == HN_NDIS_TXCSUM_CAP_TCP6)
                dev_info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_TCP_CKSUM;
 
-       if ((hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) &&
-           (hwcaps.ndis_csum.ndis_ip6_txcsum & NDIS_TXCSUM_CAP_UDP6))
+       if ((hwcaps->ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) &&
+           (hwcaps->ndis_csum.ndis_ip6_txcsum & NDIS_TXCSUM_CAP_UDP6))
                dev_info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_UDP_CKSUM;
 
-       if ((hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) &&
-           (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6)
+       if ((hwcaps->ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) &&
+           (hwcaps->ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6)
            == HN_NDIS_LSOV2_CAP_IP6)
                dev_info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_TCP_TSO;
 
        dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_VLAN_STRIP |
                                    RTE_ETH_RX_OFFLOAD_RSS_HASH;
 
-       if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)
+       if (hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)
                dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_IPV4_CKSUM;
 
-       if ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) &&
-           (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6))
+       if ((hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) &&
+           (hwcaps->ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6))
                dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_TCP_CKSUM;
 
-       if ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) &&
-           (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6))
+       if ((hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) &&
+           (hwcaps->ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6))
                dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_UDP_CKSUM;
 
        return 0;
@@ -951,29 +936,20 @@ int hn_rndis_get_offload(struct hn_data *hv,
 uint32_t
 hn_rndis_get_ptypes(struct hn_data *hv)
 {
-       struct ndis_offload hwcaps;
+       const struct ndis_offload *hwcaps = &hv->hwcaps;
        uint32_t ptypes;
-       int error;
-
-       memset(&hwcaps, 0, sizeof(hwcaps));
-
-       error = hn_rndis_query_hwcaps(hv, &hwcaps);
-       if (error) {
-               PMD_DRV_LOG(ERR, "hwcaps query failed: %d", error);
-               return RTE_PTYPE_L2_ETHER;
-       }
 
        ptypes = RTE_PTYPE_L2_ETHER;
 
-       if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)
+       if (hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4)
                ptypes |= RTE_PTYPE_L3_IPV4;
 
-       if ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) ||
-           (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6))
+       if ((hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) ||
+           (hwcaps->ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6))
                ptypes |= RTE_PTYPE_L4_TCP;
 
-       if ((hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) ||
-           (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6))
+       if ((hwcaps->ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) ||
+           (hwcaps->ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6))
                ptypes |= RTE_PTYPE_L4_UDP;
 
        return ptypes;
@@ -1136,8 +1112,28 @@ hn_rndis_get_linkspeed(struct hn_data *hv)
 int
 hn_rndis_attach(struct hn_data *hv)
 {
+       int error;
+
        /* Initialize RNDIS. */
-       return hn_rndis_init(hv);
+       error = hn_rndis_init(hv);
+       if (error)
+               return error;
+
+       /*
+        * Query and cache hardware offload capabilities.
+        * This avoids the need to query hwcaps on each call to
+        * hn_rndis_get_offload(), hn_rndis_conf_offload(), or
+        * hn_rndis_get_ptypes(), which can cause failures if
+        * multiple threads try to query simultaneously.
+        */
+       memset(&hv->hwcaps, 0, sizeof(hv->hwcaps));
+       error = hn_rndis_query_hwcaps(hv, &hv->hwcaps);
+       if (error) {
+               PMD_DRV_LOG(ERR, "failed to query hwcaps: %d", error);
+               return error;
+       }
+
+       return 0;
 }
 
 void
diff --git a/drivers/net/netvsc/hn_var.h b/drivers/net/netvsc/hn_var.h
index 17c1d5d07b..ae7f41dc94 100644
--- a/drivers/net/netvsc/hn_var.h
+++ b/drivers/net/netvsc/hn_var.h
@@ -9,6 +9,8 @@
 #include <rte_eal_paging.h>
 #include <ethdev_driver.h>
 
+#include "ndis.h"
+
 /*
  * Tunable ethdev params
  */
@@ -151,6 +153,8 @@ struct hn_data {
        uint16_t        num_queues;
        uint64_t        rss_offloads;
 
+       struct ndis_offload     hwcaps;         /* Cached hardware offload caps 
*/
+
        rte_spinlock_t  chim_lock;
        struct rte_mem_resource chim_res;       /* UIO resource for Tx */
        struct rte_bitmap *chim_bmap;           /* Send buffer map */
-- 
2.51.0

Reply via email to