]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
pcnet32: stop holding device spin lock during napi_complete_done
authorOscar Maes <oscmaes92@gmail.com>
Thu, 28 May 2026 14:03:20 +0000 (16:03 +0200)
committerJakub Kicinski <kuba@kernel.org>
Tue, 2 Jun 2026 18:26:30 +0000 (11:26 -0700)
napi_complete_done may call gro_flush_normal (though not currently, as GRO
is unsupported at the moment), which may result in packet TX. This will
eventually result in calling pcnet32_start_xmit - resulting in a deadlock
while trying to re-acquire the already locked spin lock.

It is safe to split the spinlock block into two, because the hardware
registers are still protected from concurrent access, and the two blocks
perform unrelated operations that don't need to happen atomically.

Fixes: 5b2ec6f2be51 ("pcnet32: use napi_complete_done()")
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Oscar Maes <oscmaes92@gmail.com>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Link: https://patch.msgid.link/20260528140320.5556-1-oscmaes92@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/amd/pcnet32.c

index 911808ab13a79d2494c4d7250d0c851f53c40f78..4f3076d4ea34ebaf12a811d480049c640325263d 100644 (file)
@@ -1407,8 +1407,10 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)
                pcnet32_restart(dev, CSR0_START);
                netif_wake_queue(dev);
        }
+       spin_unlock_irqrestore(&lp->lock, flags);
 
        if (work_done < budget && napi_complete_done(napi, work_done)) {
+               spin_lock_irqsave(&lp->lock, flags);
                /* clear interrupt masks */
                val = lp->a->read_csr(ioaddr, CSR3);
                val &= 0x00ff;
@@ -1416,9 +1418,9 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)
 
                /* Set interrupt enable. */
                lp->a->write_csr(ioaddr, CSR0, CSR0_INTEN);
+               spin_unlock_irqrestore(&lp->lock, flags);
        }
 
-       spin_unlock_irqrestore(&lp->lock, flags);
        return work_done;
 }