patch 'net/netvsc: fix race conditions on VF add/remove events' has been queued to stable release 23.11.7
Shani Peretz
shperetz at nvidia.com
Wed Apr 15 11:59:30 CEST 2026
Hi,
FYI, your patch has been queued to stable release 23.11.7
Note it hasn't been pushed to http://dpdk.org/browse/dpdk-stable yet.
It will be pushed if I get no objections before 04/19/26. So please
shout if anyone has objections.
Also note that after the patch there's a diff of the upstream commit vs the
patch applied to the branch. This will indicate if there was any rebasing
needed to apply to the stable branch. If there were code changes for rebasing
(ie: not only metadata diffs), please double check that the rebase was
correctly done.
Queued patches are on a temporary branch at:
https://github.com/shanipr/dpdk-stable
This queued commit can be viewed at:
https://github.com/shanipr/dpdk-stable/commit/2e3faea858e8b8abd561c1312a709acc8c3c9506
Thanks.
Shani
---
>From 2e3faea858e8b8abd561c1312a709acc8c3c9506 Mon Sep 17 00:00:00 2001
From: Long Li <longli at microsoft.com>
Date: Thu, 26 Feb 2026 17:59:21 -0800
Subject: [PATCH] net/netvsc: fix race conditions on VF add/remove events
[ upstream commit 8bf322cdc49774c225c6d35f091a86284888e097 ]
Netvsc gets notification from VSP on VF add/remove over VMBUS, but the
timing may not match the DPDK sequence of device events triggered from
uevents from kernel.
Remove the retry logic from the code when attach to VF and rely on DPDK
event to attach to VF. With this change, both the notifications from VSP
and the DPDK will attempt a VF attach.
Also implement locking when checking on all VF related fields.
Fixes: a2a23a794b3a ("net/netvsc: support VF device hot add/remove")
Signed-off-by: Long Li <longli at microsoft.com>
---
drivers/net/netvsc/hn_ethdev.c | 8 +-
drivers/net/netvsc/hn_rxtx.c | 40 +++++----
drivers/net/netvsc/hn_var.h | 1 +
drivers/net/netvsc/hn_vf.c | 144 ++++++++++++++++++---------------
4 files changed, 106 insertions(+), 87 deletions(-)
diff --git a/drivers/net/netvsc/hn_ethdev.c b/drivers/net/netvsc/hn_ethdev.c
index 88b32e442e..c4c9d1a0a1 100644
--- a/drivers/net/netvsc/hn_ethdev.c
+++ b/drivers/net/netvsc/hn_ethdev.c
@@ -639,6 +639,9 @@ static void netvsc_hotplug_retry(void *args)
d->name);
}
+ ret = hn_vf_add(dev, hv);
+ if (ret)
+ PMD_DRV_LOG(ERR, "Failed to add VF in hotplug retry: %d", ret);
break;
}
}
@@ -1379,11 +1382,12 @@ eth_hn_dev_init(struct rte_eth_dev *eth_dev)
hv->max_queues = RTE_MIN(rxr_cnt, (unsigned int)max_chan);
/* If VF was reported but not added, do it now */
+ rte_rwlock_write_lock(&hv->vf_lock);
if (hv->vf_ctx.vf_vsp_reported && !hv->vf_ctx.vf_vsc_switched) {
PMD_INIT_LOG(DEBUG, "Adding VF device");
-
- err = hn_vf_add(eth_dev, hv);
+ err = hn_vf_add_unlocked(eth_dev, hv);
}
+ rte_rwlock_write_unlock(&hv->vf_lock);
return 0;
diff --git a/drivers/net/netvsc/hn_rxtx.c b/drivers/net/netvsc/hn_rxtx.c
index 1db0cecd96..9e8126c43a 100644
--- a/drivers/net/netvsc/hn_rxtx.c
+++ b/drivers/net/netvsc/hn_rxtx.c
@@ -1552,20 +1552,18 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
hn_process_events(hv, txq->queue_id, 0);
/* Transmit over VF if present and up */
- if (hv->vf_ctx.vf_vsc_switched) {
- rte_rwlock_read_lock(&hv->vf_lock);
- vf_dev = hn_get_vf_dev(hv);
- if (hv->vf_ctx.vf_vsc_switched && vf_dev &&
- vf_dev->data->dev_started) {
- void *sub_q = vf_dev->data->tx_queues[queue_id];
-
- nb_tx = (*vf_dev->tx_pkt_burst)
- (sub_q, tx_pkts, nb_pkts);
- rte_rwlock_read_unlock(&hv->vf_lock);
- return nb_tx;
- }
+ rte_rwlock_read_lock(&hv->vf_lock);
+ vf_dev = hn_get_vf_dev(hv);
+ if (hv->vf_ctx.vf_vsc_switched && vf_dev &&
+ vf_dev->data->dev_started) {
+ void *sub_q = vf_dev->data->tx_queues[queue_id];
+
+ nb_tx = (*vf_dev->tx_pkt_burst)
+ (sub_q, tx_pkts, nb_pkts);
rte_rwlock_read_unlock(&hv->vf_lock);
+ return nb_tx;
}
+ rte_rwlock_read_unlock(&hv->vf_lock);
for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
struct rte_mbuf *m = tx_pkts[nb_tx];
@@ -1696,17 +1694,15 @@ hn_recv_pkts(void *prxq, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
(void **)rx_pkts, nb_pkts, NULL);
/* If VF is available, check that as well */
- if (hv->vf_ctx.vf_vsc_switched) {
- rte_rwlock_read_lock(&hv->vf_lock);
- vf_dev = hn_get_vf_dev(hv);
- if (hv->vf_ctx.vf_vsc_switched && vf_dev &&
- vf_dev->data->dev_started)
- nb_rcv += hn_recv_vf(vf_dev->data->port_id, rxq,
- rx_pkts + nb_rcv,
- nb_pkts - nb_rcv);
+ rte_rwlock_read_lock(&hv->vf_lock);
+ vf_dev = hn_get_vf_dev(hv);
+ if (hv->vf_ctx.vf_vsc_switched && vf_dev &&
+ vf_dev->data->dev_started)
+ nb_rcv += hn_recv_vf(vf_dev->data->port_id, rxq,
+ rx_pkts + nb_rcv,
+ nb_pkts - nb_rcv);
+ rte_rwlock_read_unlock(&hv->vf_lock);
- rte_rwlock_read_unlock(&hv->vf_lock);
- }
return nb_rcv;
}
diff --git a/drivers/net/netvsc/hn_var.h b/drivers/net/netvsc/hn_var.h
index 628c6a5d4b..95d341b5b3 100644
--- a/drivers/net/netvsc/hn_var.h
+++ b/drivers/net/netvsc/hn_var.h
@@ -240,6 +240,7 @@ hn_get_vf_dev(const struct hn_data *hv)
int hn_vf_info_get(struct hn_data *hv,
struct rte_eth_dev_info *info);
int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv);
+int hn_vf_add_unlocked(struct rte_eth_dev *dev, struct hn_data *hv);
int hn_vf_configure_locked(struct rte_eth_dev *dev,
const struct rte_eth_conf *dev_conf);
const uint32_t *hn_vf_supported_ptypes(struct rte_eth_dev *dev);
diff --git a/drivers/net/netvsc/hn_vf.c b/drivers/net/netvsc/hn_vf.c
index 7d61376e63..145ded5eda 100644
--- a/drivers/net/netvsc/hn_vf.c
+++ b/drivers/net/netvsc/hn_vf.c
@@ -59,8 +59,8 @@ static int hn_vf_attach(struct rte_eth_dev *dev, struct hn_data *hv)
int port, ret;
if (hv->vf_ctx.vf_attached) {
- PMD_DRV_LOG(ERR, "VF already attached");
- return 0;
+ PMD_DRV_LOG(NOTICE, "VF already attached");
+ return -EEXIST;
}
port = hn_vf_match(dev);
@@ -91,10 +91,30 @@ static int hn_vf_attach(struct rte_eth_dev *dev, struct hn_data *hv)
PMD_DRV_LOG(DEBUG, "Attach VF device %u", port);
hv->vf_ctx.vf_attached = true;
hv->vf_ctx.vf_port = port;
+
+ ret = rte_eth_dev_callback_register(hv->vf_ctx.vf_port,
+ RTE_ETH_EVENT_INTR_RMV,
+ hn_eth_rmv_event_callback,
+ hv);
+ if (ret) {
+ /* Rollback state changes on callback registration failure */
+ hv->vf_ctx.vf_attached = false;
+ hv->vf_ctx.vf_port = 0;
+
+ /* Release port ownership */
+ if (rte_eth_dev_owner_unset(port, hv->owner.id) < 0)
+ PMD_DRV_LOG(ERR, "Failed to unset owner for port %d", port);
+
+ PMD_DRV_LOG(ERR,
+ "Registering callback failed for vf port %d ret %d",
+ port, ret);
+ return ret;
+ }
+
return 0;
}
-static void hn_vf_remove(struct hn_data *hv);
+static void hn_vf_remove_unlocked(struct hn_data *hv);
static void hn_remove_delayed(void *args)
{
@@ -104,12 +124,12 @@ static void hn_remove_delayed(void *args)
int ret;
bool all_eth_removed;
- /* Tell VSP to switch data path to synthetic */
- hn_vf_remove(hv);
-
PMD_DRV_LOG(NOTICE, "Start to remove port %d", port_id);
rte_rwlock_write_lock(&hv->vf_lock);
+ /* Tell VSP to switch data path to synthetic */
+ hn_vf_remove_unlocked(hv);
+
/* Give back ownership */
ret = rte_eth_dev_owner_unset(port_id, hv->owner.id);
if (ret)
@@ -217,36 +237,38 @@ static int hn_setup_vf_queues(int port, struct rte_eth_dev *dev)
return ret;
}
-int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv);
+int hn_vf_configure(struct rte_eth_dev *dev,
+ const struct rte_eth_conf *dev_conf);
-static void hn_vf_add_retry(void *args)
+/* Undo hn_vf_attach() on configure/start failure */
+static void hn_vf_detach(struct hn_data *hv)
{
- struct rte_eth_dev *dev = args;
- struct hn_data *hv = dev->data->dev_private;
+ uint16_t port = hv->vf_ctx.vf_port;
- hn_vf_add(dev, hv);
-}
+ rte_eth_dev_callback_unregister(port, RTE_ETH_EVENT_INTR_RMV,
+ hn_eth_rmv_event_callback, hv);
-int hn_vf_configure(struct rte_eth_dev *dev,
- const struct rte_eth_conf *dev_conf);
+ if (rte_eth_dev_owner_unset(port, hv->owner.id) < 0)
+ PMD_DRV_LOG(ERR, "Failed to unset owner for port %d", port);
-/* Add new VF device to synthetic device */
-int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
+ hv->vf_ctx.vf_attached = false;
+ hv->vf_ctx.vf_port = 0;
+}
+
+/* Add new VF device to synthetic device, unlocked version */
+int hn_vf_add_unlocked(struct rte_eth_dev *dev, struct hn_data *hv)
{
- int ret, port;
+ int ret = 0, port;
+ bool fresh_attach;
if (!hv->vf_ctx.vf_vsp_reported || hv->vf_ctx.vf_vsc_switched)
- return 0;
-
- rte_rwlock_write_lock(&hv->vf_lock);
+ goto exit;
ret = hn_vf_attach(dev, hv);
- if (ret) {
- PMD_DRV_LOG(NOTICE,
- "RNDIS reports VF but device not found, retrying");
- rte_eal_alarm_set(1000000, hn_vf_add_retry, dev);
+ if (ret && ret != -EEXIST)
goto exit;
- }
+
+ fresh_attach = (ret == 0);
port = hv->vf_ctx.vf_port;
@@ -256,7 +278,7 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
if (dev->data->dev_started) {
if (rte_eth_devices[port].data->dev_started) {
PMD_DRV_LOG(ERR, "VF already started on hot add");
- goto exit;
+ goto switch_data_path;
}
PMD_DRV_LOG(NOTICE, "configuring VF port %d", port);
@@ -264,7 +286,7 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
if (ret) {
PMD_DRV_LOG(ERR, "Failed to configure VF port %d",
port);
- goto exit;
+ goto detach;
}
ret = hn_setup_vf_queues(port, dev);
@@ -272,13 +294,13 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
PMD_DRV_LOG(ERR,
"Failed to configure VF queues port %d",
port);
- goto exit;
+ goto detach;
}
ret = rte_eth_dev_set_mtu(port, dev->data->mtu);
if (ret) {
PMD_DRV_LOG(ERR, "Failed to set VF MTU");
- goto exit;
+ goto detach;
}
PMD_DRV_LOG(NOTICE, "Starting VF port %d", port);
@@ -286,40 +308,52 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
if (ret) {
PMD_DRV_LOG(ERR, "rte_eth_dev_start failed ret=%d",
ret);
- goto exit;
+ goto detach;
}
hv->vf_ctx.vf_state = vf_started;
}
+switch_data_path:
ret = hn_nvs_set_datapath(hv, NVS_DATAPATH_VF);
if (ret == 0)
hv->vf_ctx.vf_vsc_switched = true;
exit:
- rte_rwlock_write_unlock(&hv->vf_lock);
+ return ret;
+
+detach:
+ if (fresh_attach)
+ hn_vf_detach(hv);
return ret;
}
-/* Switch data path to VF device */
-static void hn_vf_remove(struct hn_data *hv)
+/* Add new VF device to synthetic device, locked version */
+int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
{
int ret;
- if (!hv->vf_ctx.vf_vsc_switched) {
- PMD_DRV_LOG(ERR, "VF path not active");
- return;
- }
-
rte_rwlock_write_lock(&hv->vf_lock);
+ ret = hn_vf_add_unlocked(dev, hv);
+ rte_rwlock_write_unlock(&hv->vf_lock);
+
+ return ret;
+}
+
+/* Switch data path to synthetic, unlocked version */
+static void hn_vf_remove_unlocked(struct hn_data *hv)
+{
+ int ret;
+
if (!hv->vf_ctx.vf_vsc_switched) {
PMD_DRV_LOG(ERR, "VF path not active");
} else {
/* Stop incoming packets from arriving on VF */
ret = hn_nvs_set_datapath(hv, NVS_DATAPATH_SYNTHETIC);
if (ret == 0)
+
+ /* Clear switched flag regardless — VF is being removed */
hv->vf_ctx.vf_vsc_switched = false;
}
- rte_rwlock_write_unlock(&hv->vf_lock);
}
/* Handle VF association message from host */
@@ -341,14 +375,17 @@ hn_nvs_handle_vfassoc(struct rte_eth_dev *dev,
vf_assoc->allocated ? "add to" : "remove from",
dev->data->port_id);
- hv->vf_ctx.vf_vsp_reported = vf_assoc->allocated;
+ rte_rwlock_write_lock(&hv->vf_lock);
+ hv->vf_ctx.vf_vsp_reported = vf_assoc->allocated;
if (dev->state == RTE_ETH_DEV_ATTACHED) {
if (vf_assoc->allocated)
- hn_vf_add(dev, hv);
+ hn_vf_add_unlocked(dev, hv);
else
- hn_vf_remove(hv);
+ hn_vf_remove_unlocked(hv);
}
+
+ rte_rwlock_write_unlock(&hv->vf_lock);
}
static void
@@ -429,29 +466,12 @@ int hn_vf_configure(struct rte_eth_dev *dev,
vf_conf.intr_conf.rmv = 1;
if (hv->vf_ctx.vf_attached) {
- ret = rte_eth_dev_callback_register(hv->vf_ctx.vf_port,
- RTE_ETH_EVENT_INTR_RMV,
- hn_eth_rmv_event_callback,
- hv);
- if (ret) {
- PMD_DRV_LOG(ERR,
- "Registering callback failed for vf port %d ret %d",
- hv->vf_ctx.vf_port, ret);
- return ret;
- }
-
ret = rte_eth_dev_configure(hv->vf_ctx.vf_port,
dev->data->nb_rx_queues,
dev->data->nb_tx_queues,
&vf_conf);
if (ret) {
PMD_DRV_LOG(ERR, "VF configuration failed: %d", ret);
-
- rte_eth_dev_callback_unregister(hv->vf_ctx.vf_port,
- RTE_ETH_EVENT_INTR_RMV,
- hn_eth_rmv_event_callback,
- hv);
-
return ret;
}
@@ -556,9 +576,7 @@ int hn_vf_close(struct rte_eth_dev *dev)
int ret = 0;
struct hn_data *hv = dev->data->dev_private;
- rte_eal_alarm_cancel(hn_vf_add_retry, dev);
-
- rte_rwlock_read_lock(&hv->vf_lock);
+ rte_rwlock_write_lock(&hv->vf_lock);
if (hv->vf_ctx.vf_attached) {
rte_eth_dev_callback_unregister(hv->vf_ctx.vf_port,
RTE_ETH_EVENT_INTR_RMV,
@@ -568,7 +586,7 @@ int hn_vf_close(struct rte_eth_dev *dev)
ret = rte_eth_dev_close(hv->vf_ctx.vf_port);
hv->vf_ctx.vf_attached = false;
}
- rte_rwlock_read_unlock(&hv->vf_lock);
+ rte_rwlock_write_unlock(&hv->vf_lock);
return ret;
}
--
2.43.0
---
Diff of the applied patch vs upstream commit (please double-check if non-empty:
---
--- - 2026-04-14 14:44:31.737964885 +0300
+++ 0029-net-netvsc-fix-race-conditions-on-VF-add-remove-even.patch 2026-04-14 14:44:28.543432000 +0300
@@ -1 +1 @@
-From 8bf322cdc49774c225c6d35f091a86284888e097 Mon Sep 17 00:00:00 2001
+From 2e3faea858e8b8abd561c1312a709acc8c3c9506 Mon Sep 17 00:00:00 2001
@@ -5,0 +6,2 @@
+[ upstream commit 8bf322cdc49774c225c6d35f091a86284888e097 ]
+
@@ -17 +18,0 @@
-Cc: stable at dpdk.org
@@ -28 +29 @@
-index 6584819f4f..b51c11554c 100644
+index 88b32e442e..c4c9d1a0a1 100644
@@ -31,3 +32,3 @@
-@@ -672,6 +672,9 @@ static void netvsc_hotplug_retry(void *args)
-
- free(drv_str);
+@@ -639,6 +639,9 @@ static void netvsc_hotplug_retry(void *args)
+ d->name);
+ }
@@ -41 +42 @@
-@@ -1412,11 +1415,12 @@ eth_hn_dev_init(struct rte_eth_dev *eth_dev)
+@@ -1379,11 +1382,12 @@ eth_hn_dev_init(struct rte_eth_dev *eth_dev)
@@ -57 +58 @@
-index 72dab26ede..0d770d1b25 100644
+index 1db0cecd96..9e8126c43a 100644
@@ -60 +61 @@
-@@ -1540,20 +1540,18 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+@@ -1552,20 +1552,18 @@ hn_xmit_pkts(void *ptxq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
@@ -91 +92 @@
-@@ -1684,17 +1682,15 @@ hn_recv_pkts(void *prxq, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
+@@ -1696,17 +1694,15 @@ hn_recv_pkts(void *prxq, struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
@@ -118 +119 @@
-index 17c1d5d07b..ef55dee28e 100644
+index 628c6a5d4b..95d341b5b3 100644
@@ -121 +122 @@
-@@ -239,6 +239,7 @@ hn_get_vf_dev(const struct hn_data *hv)
+@@ -240,6 +240,7 @@ hn_get_vf_dev(const struct hn_data *hv)
@@ -128 +129 @@
- const uint32_t *hn_vf_supported_ptypes(struct rte_eth_dev *dev,
+ const uint32_t *hn_vf_supported_ptypes(struct rte_eth_dev *dev);
@@ -130 +131 @@
-index 0ecfaf54ea..dfd328d550 100644
+index 7d61376e63..145ded5eda 100644
@@ -192 +193 @@
-@@ -213,36 +233,38 @@ static int hn_setup_vf_queues(int port, struct rte_eth_dev *dev)
+@@ -217,36 +237,38 @@ static int hn_setup_vf_queues(int port, struct rte_eth_dev *dev)
@@ -250 +251 @@
-@@ -252,7 +274,7 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
+@@ -256,7 +278,7 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
@@ -259 +260 @@
-@@ -260,7 +282,7 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
+@@ -264,7 +286,7 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
@@ -268 +269 @@
-@@ -268,13 +290,13 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
+@@ -272,13 +294,13 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
@@ -284 +285 @@
-@@ -282,31 +304,42 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
+@@ -286,40 +308,52 @@ int hn_vf_add(struct rte_eth_dev *dev, struct hn_data *hv)
@@ -336 +337 @@
-@@ -314,9 +347,10 @@ static void hn_vf_remove(struct hn_data *hv)
+ /* Stop incoming packets from arriving on VF */
@@ -338,2 +339 @@
- if (ret)
- PMD_DRV_LOG(ERR, "Failed to switch to synthetic");
+ if (ret == 0)
@@ -342 +342 @@
- hv->vf_ctx.vf_vsc_switched = false;
+ hv->vf_ctx.vf_vsc_switched = false;
@@ -348 +348 @@
-@@ -338,14 +372,17 @@ hn_nvs_handle_vfassoc(struct rte_eth_dev *dev,
+@@ -341,14 +375,17 @@ hn_nvs_handle_vfassoc(struct rte_eth_dev *dev,
@@ -369 +369 @@
-@@ -426,29 +463,12 @@ int hn_vf_configure(struct rte_eth_dev *dev,
+@@ -429,29 +466,12 @@ int hn_vf_configure(struct rte_eth_dev *dev,
@@ -399 +399 @@
-@@ -555,9 +575,7 @@ int hn_vf_close(struct rte_eth_dev *dev)
+@@ -556,9 +576,7 @@ int hn_vf_close(struct rte_eth_dev *dev)
@@ -410 +410 @@
-@@ -567,7 +585,7 @@ int hn_vf_close(struct rte_eth_dev *dev)
+@@ -568,7 +586,7 @@ int hn_vf_close(struct rte_eth_dev *dev)
More information about the stable
mailing list