VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-45907

CVE-2026-45907

Description

In the Linux kernel, the following vulnerability has been resolved:

net/mlx5e: Fix deadlocks between devlink and netdev instance locks

In the mentioned "Fixes" commit, various work tasks triggering devlink health reporter recovery were switched to use netdev_trylock to protect against concurrent tear down of the channels being recovered. But this had the side effect of introducing potential deadlocks because of incorrect lock ordering.

The correct lock order is described by the init flow: probe_one -> mlx5_init_one (acquires devlink lock) -> mlx5_init_one_devl_locked -> mlx5_register_device -> mlx5_rescan_drivers_locked -...-> mlx5e_probe -> _mlx5e_probe -> register_netdev (acquires rtnl lock) -> register_netdevice (acquires netdev lock) => devlink lock -> rtnl lock -> netdev lock.

But in the current recovery flow, the order is wrong: mlx5e_tx_err_cqe_work (acquires netdev lock) -> mlx5e_reporter_tx_err_cqe -> mlx5e_health_report -> devlink_health_report (acquires devlink lock => boom!) -> devlink_health_reporter_recover -> mlx5e_tx_reporter_recover -> mlx5e_tx_reporter_recover_from_ctx -> mlx5e_tx_reporter_err_cqe_recover

The same pattern exists in: mlx5e_reporter_rx_timeout mlx5e_reporter_tx_ptpsq_unhealthy mlx5e_reporter_tx_timeout

Fix these by moving the netdev_trylock calls from the work handlers lower in the call stack, in the respective recovery functions, where they are actually necessary.

Affected products

1

Patches

6
4329514c61ab

net/mlx5e: Fix deadlocks between devlink and netdev instance locks

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitCosmin RatiuFeb 18, 2026Fixed in 6.18.14via kernel-cna
8 files changed · +122 118
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 59e17b41c3a677..cb993ad2d9ad9e 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -688,19 +688,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1997,20 +1985,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5102,19 +5077,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5127,8 +5089,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 59e17b41c3a677..cb993ad2d9ad9e 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -688,19 +688,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1997,20 +1985,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5102,19 +5077,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5127,8 +5089,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index c93ee969ea6477..ec715b158a3421 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -448,22 +448,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index c93ee969ea6477..ec715b158a3421 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -448,22 +448,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index b1415992ffa244..a09a7c05820d68 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index b1415992ffa244..a09a7c05820d68 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 9e2cf191ed3086..9f6454102cf799 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -78,6 +80,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -113,9 +127,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -136,10 +152,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -147,7 +177,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -155,7 +185,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -172,10 +203,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -192,6 +235,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 9e2cf191ed3086..9f6454102cf799 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -78,6 +80,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -113,9 +127,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -136,10 +152,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -147,7 +177,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -155,7 +185,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -172,10 +203,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -192,6 +235,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
63f9d5fb4d80

net/mlx5e: Fix deadlocks between devlink and netdev instance locks

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitCosmin RatiuFeb 18, 2026Fixed in 6.19.4via kernel-cna
8 files changed · +122 118
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 4b2963bbe7ff45..e15e6fb4cd8ead 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -688,19 +688,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1997,20 +1985,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5121,19 +5096,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5146,8 +5108,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 4b2963bbe7ff45..e15e6fb4cd8ead 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -688,19 +688,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1997,20 +1985,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5121,19 +5096,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5146,8 +5108,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index 424f8a2728a3ef..74660e7fe6748b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -457,22 +457,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index 424f8a2728a3ef..74660e7fe6748b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -457,22 +457,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index 0686fbdd5a0599..6efb626b55062b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index 0686fbdd5a0599..6efb626b55062b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 9e2cf191ed3086..9f6454102cf799 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -78,6 +80,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -113,9 +127,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -136,10 +152,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -147,7 +177,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -155,7 +185,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -172,10 +203,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -192,6 +235,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 9e2cf191ed3086..9f6454102cf799 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -78,6 +80,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -113,9 +127,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -136,10 +152,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -147,7 +177,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -155,7 +185,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -172,10 +203,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -192,6 +235,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
83ac0304a2d7

net/mlx5e: Fix deadlocks between devlink and netdev instance locks

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitCosmin RatiuFeb 18, 2026Fixed in 7.0via kernel-cna
8 files changed · +122 118
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 6a7ca4571c193d..7eb691c2a1bd71 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -631,19 +631,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1952,20 +1940,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5115,19 +5090,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5140,8 +5102,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 6a7ca4571c193d..7eb691c2a1bd71 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -631,19 +631,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1952,20 +1940,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5115,19 +5090,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5140,8 +5102,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index 424f8a2728a3ef..74660e7fe6748b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -457,22 +457,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index 424f8a2728a3ef..74660e7fe6748b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -457,22 +457,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index 0686fbdd5a0599..6efb626b55062b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index 0686fbdd5a0599..6efb626b55062b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 4adc1adf9897a9..60ba840e00fa3d 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -79,6 +81,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -114,9 +128,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -137,10 +153,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -148,7 +178,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -156,7 +186,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -173,10 +204,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -193,6 +236,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 4adc1adf9897a9..60ba840e00fa3d 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -79,6 +81,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -114,9 +128,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -137,10 +153,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -148,7 +178,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -156,7 +186,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -173,10 +204,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -193,6 +236,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
4329514c61ab

net/mlx5e: Fix deadlocks between devlink and netdev instance locks

8 files changed · +122 118
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 59e17b41c3a677..cb993ad2d9ad9e 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -688,19 +688,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1997,20 +1985,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5102,19 +5077,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5127,8 +5089,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 59e17b41c3a677..cb993ad2d9ad9e 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -688,19 +688,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1997,20 +1985,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5102,19 +5077,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5127,8 +5089,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index c93ee969ea6477..ec715b158a3421 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -448,22 +448,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index c93ee969ea6477..ec715b158a3421 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -448,22 +448,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index b1415992ffa244..a09a7c05820d68 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index b1415992ffa244..a09a7c05820d68 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 9e2cf191ed3086..9f6454102cf799 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -78,6 +80,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -113,9 +127,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -136,10 +152,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -147,7 +177,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -155,7 +185,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -172,10 +203,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -192,6 +235,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 9e2cf191ed3086..9f6454102cf799 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -78,6 +80,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -113,9 +127,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -136,10 +152,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -147,7 +177,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -155,7 +185,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -172,10 +203,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -192,6 +235,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
63f9d5fb4d80

net/mlx5e: Fix deadlocks between devlink and netdev instance locks

8 files changed · +122 118
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 4b2963bbe7ff45..e15e6fb4cd8ead 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -688,19 +688,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1997,20 +1985,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5121,19 +5096,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5146,8 +5108,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 4b2963bbe7ff45..e15e6fb4cd8ead 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -688,19 +688,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1997,20 +1985,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5121,19 +5096,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5146,8 +5108,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index 424f8a2728a3ef..74660e7fe6748b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -457,22 +457,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index 424f8a2728a3ef..74660e7fe6748b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -457,22 +457,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index 0686fbdd5a0599..6efb626b55062b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index 0686fbdd5a0599..6efb626b55062b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 9e2cf191ed3086..9f6454102cf799 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -78,6 +80,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -113,9 +127,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -136,10 +152,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -147,7 +177,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -155,7 +185,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -172,10 +203,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -192,6 +235,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 9e2cf191ed3086..9f6454102cf799 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -78,6 +80,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -113,9 +127,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -136,10 +152,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -147,7 +177,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -155,7 +185,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -172,10 +203,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -192,6 +235,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
83ac0304a2d7

net/mlx5e: Fix deadlocks between devlink and netdev instance locks

8 files changed · +122 118
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 6a7ca4571c193d..7eb691c2a1bd71 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -631,19 +631,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1952,20 +1940,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5115,19 +5090,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5140,8 +5102,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en_main.c+0 41 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    index 6a7ca4571c193d..7eb691c2a1bd71 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
    @@ -631,19 +631,7 @@ static void mlx5e_rq_timeout_work(struct work_struct *timeout_work)
     					   struct mlx5e_rq,
     					   rx_timeout_work);
     
    -	/* Acquire netdev instance lock to synchronize with channel close and
    -	 * reopen flows. Either successfully obtain the lock, or detect that
    -	 * channels are closing for another reason, making this work no longer
    -	 * necessary.
    -	 */
    -	while (!netdev_trylock(rq->netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_rx_timeout(rq);
    -	netdev_unlock(rq->netdev);
     }
     
     static int mlx5e_alloc_mpwqe_rq_drop_page(struct mlx5e_rq *rq)
    @@ -1952,20 +1940,7 @@ void mlx5e_tx_err_cqe_work(struct work_struct *recover_work)
     	struct mlx5e_txqsq *sq = container_of(recover_work, struct mlx5e_txqsq,
     					      recover_work);
     
    -	/* Recovering queues means re-enabling NAPI, which requires the netdev
    -	 * instance lock. However, SQ closing flows have to wait for work tasks
    -	 * to finish while also holding the netdev instance lock. So either get
    -	 * the lock or find that the SQ is no longer enabled and thus this work
    -	 * is not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	mlx5e_reporter_tx_err_cqe(sq);
    -	netdev_unlock(sq->netdev);
     }
     
     static struct dim_cq_moder mlx5e_get_def_tx_moderation(u8 cq_period_mode)
    @@ -5115,19 +5090,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     	struct net_device *netdev = priv->netdev;
     	int i;
     
    -	/* Recovering the TX queues implies re-enabling NAPI, which requires
    -	 * the netdev instance lock.
    -	 * However, channel closing flows have to wait for this work to finish
    -	 * while holding the same lock. So either get the lock or find that
    -	 * channels are being closed for other reason and this work is not
    -	 * relevant anymore.
    -	 */
    -	while (!netdev_trylock(netdev)) {
    -		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    -			return;
    -		msleep(20);
    -	}
    -
     	for (i = 0; i < netdev->real_num_tx_queues; i++) {
     		struct netdev_queue *dev_queue =
     			netdev_get_tx_queue(netdev, i);
    @@ -5140,8 +5102,6 @@ static void mlx5e_tx_timeout_work(struct work_struct *work)
     		/* break if tried to reopened channels */
     			break;
     	}
    -
    -	netdev_unlock(netdev);
     }
     
     static void mlx5e_tx_timeout(struct net_device *dev, unsigned int txqueue)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index 424f8a2728a3ef..74660e7fe6748b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -457,22 +457,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c+0 14 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    index 424f8a2728a3ef..74660e7fe6748b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
    @@ -457,22 +457,8 @@ static void mlx5e_ptpsq_unhealthy_work(struct work_struct *work)
     {
     	struct mlx5e_ptpsq *ptpsq =
     		container_of(work, struct mlx5e_ptpsq, report_unhealthy_work);
    -	struct mlx5e_txqsq *sq = &ptpsq->txqsq;
    -
    -	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    -	 * netdev instance lock. However, SQ closing has to wait for this work
    -	 * task to finish while also holding the same lock. So either get the
    -	 * lock or find that the SQ is no longer enabled and thus this work is
    -	 * not relevant anymore.
    -	 */
    -	while (!netdev_trylock(sq->netdev)) {
    -		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    -			return;
    -		msleep(20);
    -	}
     
     	mlx5e_reporter_tx_ptpsq_unhealthy(ptpsq);
    -	netdev_unlock(sq->netdev);
     }
     
     static int mlx5e_ptp_open_txqsq(struct mlx5e_ptp *c, u32 tisn,
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index 0686fbdd5a0599..6efb626b55062b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c+13 0 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    index 0686fbdd5a0599..6efb626b55062b 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
    @@ -1,6 +1,8 @@
     // SPDX-License-Identifier: GPL-2.0
     // Copyright (c) 2019 Mellanox Technologies.
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "params.h"
     #include "txrx.h"
    @@ -177,6 +179,16 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     	rq = ctx;
     	priv = rq->priv;
     
    +	/* Acquire netdev instance lock to synchronize with channel close and
    +	 * reopen flows. Either successfully obtain the lock, or detect that
    +	 * channels are closing for another reason, making this work no longer
    +	 * necessary.
    +	 */
    +	while (!netdev_trylock(rq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &rq->priv->state))
    +			return 0;
    +		msleep(20);
    +	}
     	mutex_lock(&priv->state_lock);
     
     	eq = rq->cq.mcq.eq;
    @@ -186,6 +198,7 @@ static int mlx5e_rx_reporter_timeout_recover(void *ctx)
     		clear_bit(MLX5E_SQ_STATE_ENABLED, &rq->icosq->state);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(rq->netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 4adc1adf9897a9..60ba840e00fa3d 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -79,6 +81,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -114,9 +128,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -137,10 +153,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -148,7 +178,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -156,7 +186,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -173,10 +204,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -193,6 +236,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    
  • drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c+48 4 modified
    diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    index 4adc1adf9897a9..60ba840e00fa3d 100644
    --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
    @@ -1,6 +1,8 @@
     /* SPDX-License-Identifier: GPL-2.0 */
     /* Copyright (c) 2019 Mellanox Technologies. */
     
    +#include <net/netdev_lock.h>
    +
     #include "health.h"
     #include "en/ptp.h"
     #include "en/devlink.h"
    @@ -79,6 +81,18 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
     		return 0;
     
    +	/* Recovering queues means re-enabling NAPI, which requires the netdev
    +	 * instance lock. However, SQ closing flows have to wait for work tasks
    +	 * to finish while also holding the netdev instance lock. So either get
    +	 * the lock or find that the SQ is no longer enabled and thus this work
    +	 * is not relevant anymore.
    +	 */
    +	while (!netdev_trylock(dev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
     	if (err) {
     		netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
    @@ -114,9 +128,11 @@ static int mlx5e_tx_reporter_err_cqe_recover(void *ctx)
     	else
     		mlx5e_trigger_napi_sched(sq->cq.napi);
     
    +	netdev_unlock(dev);
     	return 0;
     out:
     	clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
    +	netdev_unlock(dev);
     	return err;
     }
     
    @@ -137,10 +153,24 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	sq = to_ctx->sq;
     	eq = sq->cq.mcq.eq;
     	priv = sq->priv;
    +
    +	/* Recovering the TX queues implies re-enabling NAPI, which requires
    +	 * the netdev instance lock.
    +	 * However, channel closing flows have to wait for this work to finish
    +	 * while holding the same lock. So either get the lock or find that
    +	 * channels are being closed for other reason and this work is not
    +	 * relevant anymore.
    +	 */
    +	while (!netdev_trylock(sq->netdev)) {
    +		if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
    +			return 0;
    +		msleep(20);
    +	}
    +
     	err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
     	if (!err) {
     		to_ctx->status = 0; /* this sq recovered */
    -		return err;
    +		goto out;
     	}
     
     	mutex_lock(&priv->state_lock);
    @@ -148,7 +178,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	mutex_unlock(&priv->state_lock);
     	if (!err) {
     		to_ctx->status = 1; /* all channels recovered */
    -		return err;
    +		goto out;
     	}
     
     	to_ctx->status = err;
    @@ -156,7 +186,8 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
     	netdev_err(priv->netdev,
     		   "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
     		   err);
    -
    +out:
    +	netdev_unlock(sq->netdev);
     	return err;
     }
     
    @@ -173,10 +204,22 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		return 0;
     
     	priv = ptpsq->txqsq.priv;
    +	netdev = priv->netdev;
    +
    +	/* Recovering the PTP SQ means re-enabling NAPI, which requires the
    +	 * netdev instance lock. However, SQ closing has to wait for this work
    +	 * task to finish while also holding the same lock. So either get the
    +	 * lock or find that the SQ is no longer enabled and thus this work is
    +	 * not relevant anymore.
    +	 */
    +	while (!netdev_trylock(netdev)) {
    +		if (!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))
    +			return 0;
    +		msleep(20);
    +	}
     
     	mutex_lock(&priv->state_lock);
     	chs = &priv->channels;
    -	netdev = priv->netdev;
     
     	carrier_ok = netif_carrier_ok(netdev);
     	netif_carrier_off(netdev);
    @@ -193,6 +236,7 @@ static int mlx5e_tx_reporter_ptpsq_unhealthy_recover(void *ctx)
     		netif_carrier_on(netdev);
     
     	mutex_unlock(&priv->state_lock);
    +	netdev_unlock(netdev);
     
     	return err;
     }
    

Vulnerability mechanics

Root cause

"Incorrect lock ordering in mlx5e devlink health recovery work handlers causes a deadlock between the netdev instance lock and the devlink lock."

Attack vector

An attacker can trigger a deadlock by causing a hardware error that invokes the devlink health reporter recovery path. The work handlers (e.g., `mlx5e_tx_err_cqe_work`) first acquire the netdev instance lock, then call into `devlink_health_report`, which acquires the devlink lock — reversing the correct lock ordering of devlink lock → rtnl lock → netdev lock [patch_id=2661481]. If another thread concurrently holds the devlink lock and waits for the netdev lock (e.g., during probe or channel close), the two threads deadlock. No authentication is required; any condition that triggers a TX completion error, RX timeout, TX timeout, or PTP SQ unhealthy event on a mlx5e netdev can initiate the deadlock sequence.

Affected code

The vulnerability affects the mlx5e driver in the Linux kernel, specifically in `drivers/net/ethernet/mellanox/mlx5/core/en_main.c` (work handlers `mlx5e_rq_timeout_work`, `mlx5e_tx_err_cqe_work`, `mlx5e_tx_timeout_work`), `drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c` (`mlx5e_ptpsq_unhealthy_work`), and the recovery functions in `drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c` and `drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c` [patch_id=2661481].

What the fix does

The patch moves the `netdev_trylock`/`netdev_unlock` calls from the work handler functions (e.g., `mlx5e_tx_err_cqe_work`, `mlx5e_rq_timeout_work`, `mlx5e_ptpsq_unhealthy_work`, `mlx5e_tx_timeout_work`) down into the actual recovery functions (`mlx5e_tx_reporter_err_cqe_recover`, `mlx5e_rx_reporter_timeout_recover`, `mlx5e_tx_reporter_ptpsq_unhealthy_recover`, `mlx5e_tx_reporter_timeout_recover`) [patch_id=2661481]. This ensures the netdev lock is acquired only after the devlink lock has already been taken by `devlink_health_reporter_recover`, restoring the correct lock ordering (devlink → netdev) and eliminating the deadlock [patch_id=2661481]. The `#include <net/netdev_lock.h>` header is added to the reporter files where it was previously absent [patch_id=2661481].

Preconditions

  • configThe system must be running a mlx5e (Mellanox ConnectX) network driver
  • inputA hardware error or timeout must occur that triggers the devlink health reporter recovery path
  • inputAnother thread must concurrently hold the devlink lock and wait for the netdev instance lock

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.