The dm9000 takes the db->lock spin lock in dm9000_timeout() and calls into dm9000_init_dm9000(). For the DM9000B the PHY is reset with dm9000_phy_write(). That function again takes the db->lock spin lock, which results in a deadlock. For reference the backtrace:
| [<c0425050>] (rt_spin_lock_slowlock_locked) from [<c0425100>] (rt_spin_lock_slowlock+0x60/0xc4) | [<c0425100>] (rt_spin_lock_slowlock) from [<c02e1174>] (dm9000_phy_write+0x2c/0x1a4) | [<c02e1174>] (dm9000_phy_write) from [<c02e16b0>] (dm9000_init_dm9000+0x288/0x2a4) | [<c02e16b0>] (dm9000_init_dm9000) from [<c02e1724>] (dm9000_timeout+0x58/0xd4) | [<c02e1724>] (dm9000_timeout) from [<c036f298>] (dev_watchdog+0x258/0x2a8) | [<c036f298>] (dev_watchdog) from [<c0068168>] (call_timer_fn+0x20/0x88) | [<c0068168>] (call_timer_fn) from [<c00687c8>] (expire_timers+0xf0/0x194) | [<c00687c8>] (expire_timers) from [<c0068920>] (run_timer_softirq+0xb4/0x25c) | [<c0068920>] (run_timer_softirq) from [<c0021a30>] (do_current_softirqs+0x16c/0x228) | [<c0021a30>] (do_current_softirqs) from [<c0021b14>] (run_ksoftirqd+0x28/0x4c) | [<c0021b14>] (run_ksoftirqd) from [<c0040488>] (smpboot_thread_fn+0x278/0x290) | [<c0040488>] (smpboot_thread_fn) from [<c003c28c>] (kthread+0x124/0x164) | [<c003c28c>] (kthread) from [<c00090f0>] (ret_from_fork+0x14/0x24)
To workaround similar problem (take mutex inside spin lock ) , a "in_timeout" variable was added in 582379839bbd ("dm9000: avoid sleeping in dm9000_timeout callback"). Use this variable and not take the spin lock inside dm9000_phy_write() if in_timeout is true.
Cc: stable@vger.kernel.org Signed-off-by: Marc Kleine-Budde mkl@pengutronix.de --- During the netdev watchdog handling the dm9000 driver takes the same spin lock twice. Avoid this by extending an existing workaround. --- drivers/net/ethernet/davicom/dm9000.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index 05a89ab6766c..3a056a54aaf9 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -325,10 +325,10 @@ dm9000_phy_write(struct net_device *dev, unsigned long reg_save;
dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n", reg, value); - if (!db->in_timeout) + if (!db->in_timeout) { mutex_lock(&db->addr_lock); - - spin_lock_irqsave(&db->lock, flags); + spin_lock_irqsave(&db->lock, flags); + }
/* Save previous register address */ reg_save = readb(db->io_addr); @@ -344,11 +344,13 @@ dm9000_phy_write(struct net_device *dev, iow(db, DM9000_EPCR, EPCR_EPOS | EPCR_ERPRW);
writeb(reg_save, db->io_addr); - spin_unlock_irqrestore(&db->lock, flags); + if (!db->in_timeout) + spin_unlock_irqrestore(&db->lock, flags);
dm9000_msleep(db, 1); /* Wait write complete */
- spin_lock_irqsave(&db->lock, flags); + if (!db->in_timeout) + spin_lock_irqsave(&db->lock, flags); reg_save = readb(db->io_addr);
iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer write command */ @@ -356,9 +358,10 @@ dm9000_phy_write(struct net_device *dev, /* restore the previous address */ writeb(reg_save, db->io_addr);
- spin_unlock_irqrestore(&db->lock, flags); - if (!db->in_timeout) + if (!db->in_timeout) { + spin_unlock_irqrestore(&db->lock, flags); mutex_unlock(&db->addr_lock); + } }
/* dm9000_set_io
--- base-commit: 776fe19953b0e0af00399e50fb3b205101d4b3c1 change-id: 20231010-dm9000-fix-deadlock-ffb1e5295ee1
Best regards,