Since commit 413f0271f396 ("net: protect NAPI enablement with netdev_lock()"), napi_enable() and napi_disable() take netdev_lock(). mal_register_commac() and mal_unregister_commac() call these helpers while holding mal->lock with interrupts disabled. In the unregister path, napi_disable() may also wait for polling to finish, while the poll completion path takes mal->lock. Take netdev_lock() before mal->lock, use the locked NAPI helpers, and drop mal->lock before napi_disable_locked(). Fixes: 413f0271f396 ("net: protect NAPI enablement with netdev_lock()") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao --- drivers/net/ethernet/ibm/emac/mal.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index 74526002d52b..42027665f2a9 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -34,6 +34,7 @@ int mal_register_commac(struct mal_instance *mal, struct mal_commac *commac) { unsigned long flags; + netdev_lock(mal->napi.dev); spin_lock_irqsave(&mal->lock, flags); MAL_DBG(mal, "reg(%08x, %08x)" NL, @@ -43,7 +44,8 @@ int mal_register_commac(struct mal_instance *mal, struct mal_commac *commac) /* Don't let multiple commacs claim the same channel(s) */ if ((mal->tx_chan_mask & commac->tx_chan_mask) || (mal->rx_chan_mask & commac->rx_chan_mask)) { spin_unlock_irqrestore(&mal->lock, flags); + netdev_unlock(mal->napi.dev); printk(KERN_WARNING "mal%d: COMMAC channels conflict!\n", mal->index); return -EBUSY; @@ -51,11 +52,12 @@ int mal_register_commac(struct mal_instance *mal, struct mal_commac *commac) if (list_empty(&mal->list)) - napi_enable(&mal->napi); + napi_enable_locked(&mal->napi); mal->tx_chan_mask |= commac->tx_chan_mask; mal->rx_chan_mask |= commac->rx_chan_mask; list_add(&commac->list, &mal->list); spin_unlock_irqrestore(&mal->lock, flags); + netdev_unlock(mal->napi.dev); return 0; } @@ -64,7 +66,9 @@ void mal_unregister_commac(struct mal_instance *mal, struct mal_commac *commac) { unsigned long flags; + bool disable_napi; + netdev_lock(mal->napi.dev); spin_lock_irqsave(&mal->lock, flags); MAL_DBG(mal, "unreg(%08x, %08x)" NL, @@ -73,10 +79,12 @@ void mal_unregister_commac(struct mal_instance *mal, mal->tx_chan_mask &= ~commac->tx_chan_mask; mal->rx_chan_mask &= ~commac->rx_chan_mask; list_del_init(&commac->list); - if (list_empty(&mal->list)) - napi_disable(&mal->napi); + disable_napi = list_empty(&mal->list); spin_unlock_irqrestore(&mal->lock, flags); + if (disable_napi) + napi_disable_locked(&mal->napi); + netdev_unlock(mal->napi.dev); } int mal_set_rcbs(struct mal_instance *mal, int channel, unsigned long size) -- 2.34.1