public inbox for dev@dpdk.org
 help / color / mirror / Atom feed
From: Stephen Hemminger <stephen@networkplumber.org>
To: dev@dpdk.org
Cc: Stephen Hemminger <stephen@networkplumber.org>,
	stable@dpdk.org, Madhuker Mythri <madhukar.mythri@gmail.com>
Subject: [RFT 2/2] net/netvsc: fix link status RNDIS command concurrency issue
Date: Tue, 13 Jan 2026 08:58:07 -0800	[thread overview]
Message-ID: <20260113170143.70873-3-stephen@networkplumber.org> (raw)
In-Reply-To: <20260113170143.70873-1-stephen@networkplumber.org>

The hn_dev_link_update() function issues RNDIS queries for link status
and link speed on every call. Since this function can be called from
multiple threads concurrently, it triggers the same RNDIS command
concurrency issue where only one request can be pending at a time.

Remove the hn_rndis_get_linkstatus() and hn_rndis_get_linkspeed()
functions and modify hn_dev_link_update() to use the cached values
directly. The link status and speed are already:

1. Queried once during device attach in hn_rndis_attach()
2. Updated via RNDIS status indications (MEDIA_CONNECT/DISCONNECT
   and LINK_SPEED_CHANGE) handled in hn_rndis_link_status()

This matches the Linux netvsc driver behavior where link status is
queried once in rndis_filter_device_add() and then tracked via
status indications.

Fixes: 4e9c73e96e83 ("net/netvsc: add Hyper-V network device")
Cc: stable@dpdk.org

Reported-by: Madhuker Mythri <madhukar.mythri@gmail.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 drivers/net/netvsc/hn_ethdev.c | 13 ++++---
 drivers/net/netvsc/hn_rndis.c  | 65 ++++++++++++++++++++++++++--------
 drivers/net/netvsc/hn_rndis.h  |  2 --
 3 files changed, 57 insertions(+), 23 deletions(-)

diff --git a/drivers/net/netvsc/hn_ethdev.c b/drivers/net/netvsc/hn_ethdev.c
index 6584819f4f..c4580d0bb3 100644
--- a/drivers/net/netvsc/hn_ethdev.c
+++ b/drivers/net/netvsc/hn_ethdev.c
@@ -246,16 +246,15 @@ hn_dev_link_update(struct rte_eth_dev *dev,
 {
 	struct hn_data *hv = dev->data->dev_private;
 	struct rte_eth_link link, old;
-	int error;
 
 	old = dev->data->dev_link;
 
-	error = hn_rndis_get_linkstatus(hv);
-	if (error)
-		return error;
-
-	hn_rndis_get_linkspeed(hv);
-
+	/*
+	 * Use cached link_status and link_speed which are set during
+	 * device attach and updated via RNDIS status indications
+	 * (MEDIA_CONNECT/DISCONNECT and LINK_SPEED_CHANGE).
+	 * This avoids RNDIS queries which are not thread-safe.
+	 */
 	link = (struct rte_eth_link) {
 		.link_duplex = RTE_ETH_LINK_FULL_DUPLEX,
 		.link_autoneg = RTE_ETH_LINK_SPEED_FIXED,
diff --git a/drivers/net/netvsc/hn_rndis.c b/drivers/net/netvsc/hn_rndis.c
index ed053ee905..c85953af34 100644
--- a/drivers/net/netvsc/hn_rndis.c
+++ b/drivers/net/netvsc/hn_rndis.c
@@ -299,6 +299,7 @@ static void hn_rndis_link_alarm(void *arg)
 void hn_rndis_link_status(struct rte_eth_dev *dev, const void *msg)
 {
 	const struct rndis_status_msg *indicate = msg;
+	struct hn_data *hv = dev->data->dev_private;
 
 	hn_rndis_dump(msg);
 
@@ -311,11 +312,41 @@ void hn_rndis_link_status(struct rte_eth_dev *dev, const void *msg)
 		break;
 
 	case RNDIS_STATUS_LINK_SPEED_CHANGE:
+		/*
+		 * Update link speed from the status buffer.
+		 * This matches the Linux netvsc driver behavior.
+		 */
+		if (indicate->stbuflen >= sizeof(uint32_t)) {
+			const uint8_t *p = (const uint8_t *)msg;
+			uint32_t offset = RNDIS_STBUFOFFSET_ABS(indicate->stbufoffset);
+
+			if (offset + sizeof(uint32_t) <= indicate->len) {
+				uint32_t speed;
+
+				memcpy(&speed, p + offset, sizeof(speed));
+				hv->link_speed = speed;
+				PMD_DRV_LOG(DEBUG, "link speed changed to %u",
+					    speed);
+			}
+		}
+		if (dev->data->dev_conf.intr_conf.lsc)
+			rte_eal_alarm_set(10, hn_rndis_link_alarm, dev);
+		break;
+
 	case RNDIS_STATUS_MEDIA_CONNECT:
+		hv->link_status = NDIS_MEDIA_STATE_CONNECTED;
+		PMD_DRV_LOG(INFO, "link connected");
+		if (dev->data->dev_conf.intr_conf.lsc)
+			rte_eal_alarm_set(10, hn_rndis_link_alarm, dev);
+		break;
+
 	case RNDIS_STATUS_MEDIA_DISCONNECT:
+		hv->link_status = NDIS_MEDIA_STATE_DISCONNECTED;
+		PMD_DRV_LOG(INFO, "link disconnected");
 		if (dev->data->dev_conf.intr_conf.lsc)
 			rte_eal_alarm_set(10, hn_rndis_link_alarm, dev);
 		break;
+
 	default:
 		PMD_DRV_LOG(NOTICE, "unknown RNDIS indication: %#x",
 			    indicate->status);
@@ -1095,20 +1126,6 @@ hn_rndis_get_mtu(struct hn_data *hv, uint32_t *mtu)
 			       mtu, sizeof(uint32_t));
 }
 
-int
-hn_rndis_get_linkstatus(struct hn_data *hv)
-{
-	return hn_rndis_query(hv, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
-			      &hv->link_status, sizeof(uint32_t));
-}
-
-int
-hn_rndis_get_linkspeed(struct hn_data *hv)
-{
-	return hn_rndis_query(hv, OID_GEN_LINK_SPEED, NULL, 0,
-			      &hv->link_speed, sizeof(uint32_t));
-}
-
 int
 hn_rndis_attach(struct hn_data *hv)
 {
@@ -1133,6 +1150,26 @@ hn_rndis_attach(struct hn_data *hv)
 		return error;
 	}
 
+	/*
+	 * Query initial link status and speed. These will be updated
+	 * via RNDIS status indications (MEDIA_CONNECT/DISCONNECT and
+	 * LINK_SPEED_CHANGE) rather than being re-queried each time.
+	 * This matches the Linux netvsc driver behavior.
+	 */
+	error = hn_rndis_query(hv, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
+			       &hv->link_status, sizeof(hv->link_status));
+	if (error) {
+		PMD_DRV_LOG(ERR, "failed to query link status: %d", error);
+		return error;
+	}
+
+	error = hn_rndis_query(hv, OID_GEN_LINK_SPEED, NULL, 0,
+			       &hv->link_speed, sizeof(hv->link_speed));
+	if (error) {
+		PMD_DRV_LOG(ERR, "failed to query link speed: %d", error);
+		return error;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/netvsc/hn_rndis.h b/drivers/net/netvsc/hn_rndis.h
index 7f40f6221d..26854f45a4 100644
--- a/drivers/net/netvsc/hn_rndis.h
+++ b/drivers/net/netvsc/hn_rndis.h
@@ -11,8 +11,6 @@ int	hn_rndis_attach(struct hn_data *hv);
 void	hn_rndis_detach(struct hn_data *hv);
 int	hn_rndis_get_eaddr(struct hn_data *hv, uint8_t *eaddr);
 int	hn_rndis_get_mtu(struct hn_data *hv, uint32_t *mtu);
-int	hn_rndis_get_linkstatus(struct hn_data *hv);
-int	hn_rndis_get_linkspeed(struct hn_data *hv);
 int	hn_rndis_set_rxfilter(struct hn_data *hv, uint32_t filter);
 void	hn_rndis_rx_ctrl(struct hn_data *hv, const void *data,
 			 int dlen);
-- 
2.51.0


  parent reply	other threads:[~2026-01-13 17:02 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-13 10:18 [PATCH v2] net/netvsc: fix race condition in RNDIS command execution madhukar.mythri
2026-01-13 16:58 ` [RFT 0/2] net/netvsc: fix race conditions Stephen Hemminger
2026-01-13 16:58   ` [RFT 1/2] net/netvsc: fix RNDIS command concurrency issue Stephen Hemminger
2026-01-13 16:58   ` Stephen Hemminger [this message]
2026-01-16 23:10 ` [EXTERNAL] [PATCH v2] net/netvsc: fix race condition in RNDIS command execution Long Li
2026-03-28 23:53 ` Stephen Hemminger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260113170143.70873-3-stephen@networkplumber.org \
    --to=stephen@networkplumber.org \
    --cc=dev@dpdk.org \
    --cc=madhukar.mythri@gmail.com \
    --cc=stable@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox