[PATCH net-next v12 4/4] net: mana: recover port on attach failure in ethtool operations

From: Dipayaan Roy

Date: Sat Jul 11 2026 - 00:16:57 EST


When mana_attach() fails during ethtool ring size or channel count
changes, the port is left in a broken state with no recovery
mechanism, requiring manual intervention to bring the port back up.

On VM SKUs without a netvsc fallback interface, this results in
complete loss of network connectivity to the VM.

Fix by scheduling queue_reset_work when mana_attach() fails. The
preceding patch ensures mana_detach() always completes its full
teardown (netif_device_detach + cleanup), so the reset handler's
mana_detach() takes the "already detached" early return, preserving
port_st_save for a successful mana_attach() recovery.

When mana_attach() fails, choose retry values that maximize recovery
chances: if the operation was an increase, fall back to the previous
working values; if it was a decrease but still above default, fall
back to defaults; otherwise use the minimum supported values.

Reviewed-by: Haiyang Zhang <haiyangz@xxxxxxxxxxxxx>
Tested-by: Aditya Garg <gargaditya@xxxxxxxxxxxxxxxxxxx>
Signed-off-by: Dipayaan Roy <dipayanroy@xxxxxxxxxxxxxxxxxxx>
---
.../ethernet/microsoft/mana/mana_ethtool.c | 48 +++++++++++++++++--
1 file changed, 45 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index f77509818d07..71e69d5a9a04 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -646,6 +646,7 @@ static int mana_set_channels(struct net_device *ndev,
struct mana_port_context *apc = netdev_priv(ndev);
unsigned int new_count = channels->combined_count;
unsigned int old_count = apc->num_queues;
+ bool schedule_port_reset = false;
int err;

/* Set channel_changing to block RDMA from grabbing the vport
@@ -675,8 +676,19 @@ static int mana_set_channels(struct net_device *ndev,
apc->num_queues = new_count;
err = mana_attach(ndev);
if (err) {
- apc->num_queues = old_count;
netdev_err(ndev, "mana_attach failed: %d\n", err);
+
+ /* Choose a retry queue count that maximizes recovery
+ * chances in the reset work handler.
+ */
+ if (old_count < new_count)
+ apc->num_queues = old_count;
+ else if (new_count > MANA_DEF_NUM_QUEUES)
+ apc->num_queues = MANA_DEF_NUM_QUEUES;
+ else
+ apc->num_queues = 1;
+
+ schedule_port_reset = true;
}

out:
@@ -685,6 +697,11 @@ static int mana_set_channels(struct net_device *ndev,
mutex_lock(&apc->vport_mutex);
apc->channel_changing = false;
mutex_unlock(&apc->vport_mutex);
+
+ if (schedule_port_reset)
+ queue_work(apc->ac->per_port_queue_reset_wq,
+ &apc->queue_reset_work);
+
return err;
}

@@ -707,6 +724,7 @@ static int mana_set_ringparam(struct net_device *ndev,
struct netlink_ext_ack *extack)
{
struct mana_port_context *apc = netdev_priv(ndev);
+ bool schedule_port_reset = false;
u32 new_tx, new_rx;
u32 old_tx, old_rx;
int err;
@@ -752,11 +770,35 @@ static int mana_set_ringparam(struct net_device *ndev,
err = mana_attach(ndev);
if (err) {
netdev_err(ndev, "mana_attach failed: %d\n", err);
- apc->tx_queue_size = old_tx;
- apc->rx_queue_size = old_rx;
+ NL_SET_ERR_MSG_FMT(extack, "failed to change ring params: %d",
+ err);
+
+ /* Choose retry ring sizes that maximize recovery
+ * chances in the reset work handler. Handle RX and
+ * TX independently.
+ */
+ if (old_rx < new_rx)
+ apc->rx_queue_size = old_rx;
+ else if (new_rx > DEF_RX_BUFFERS_PER_QUEUE)
+ apc->rx_queue_size = DEF_RX_BUFFERS_PER_QUEUE;
+ else
+ apc->rx_queue_size = MIN_RX_BUFFERS_PER_QUEUE;
+
+ if (old_tx < new_tx)
+ apc->tx_queue_size = old_tx;
+ else if (new_tx > DEF_TX_BUFFERS_PER_QUEUE)
+ apc->tx_queue_size = DEF_TX_BUFFERS_PER_QUEUE;
+ else
+ apc->tx_queue_size = MIN_TX_BUFFERS_PER_QUEUE;
+
+ schedule_port_reset = true;
}
out:
mana_pre_dealloc_rxbufs(apc);
+
+ if (schedule_port_reset)
+ queue_work(apc->ac->per_port_queue_reset_wq,
+ &apc->queue_reset_work);
return err;
}

--
2.43.0