Commit 275b471e authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller

net: don't let netpoll invoke NAPI if in xmit context

Commit 0db3dc73 ("[NETPOLL]: tx lock deadlock fix") narrowed
down the region under netif_tx_trylock() inside netpoll_send_skb().
(At that point in time netif_tx_trylock() would lock all queues of
the device.) Taking the tx lock was problematic because driver's
cleanup method may take the same lock. So the change made us hold
the xmit lock only around xmit, and expected the driver to take
care of locking within ->ndo_poll_controller().

Unfortunately this only works if netpoll isn't itself called with
the xmit lock already held. Netpoll code is careful and uses
trylock(). The drivers, however, may be using plain lock().
Printing while holding the xmit lock is going to result in rare
deadlocks.

Luckily we record the xmit lock owners, so we can scan all the queues,
the same way we scan NAPI owners. If any of the xmit locks is held
by the local CPU we better not attempt any polling.

It would be nice if we could narrow down the check to only the NAPIs
and the queue we're trying to use. I don't see a way to do that now.
Reported-by: default avatarRoman Gushchin <roman.gushchin@linux.dev>
Fixes: 0db3dc73 ("[NETPOLL]: tx lock deadlock fix")
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Reviewed-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7d63b671
......@@ -137,6 +137,20 @@ static void queue_process(struct work_struct *work)
}
}
static int netif_local_xmit_active(struct net_device *dev)
{
int i;
for (i = 0; i < dev->num_tx_queues; i++) {
struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
if (READ_ONCE(txq->xmit_lock_owner) == smp_processor_id())
return 1;
}
return 0;
}
static void poll_one_napi(struct napi_struct *napi)
{
int work;
......@@ -183,7 +197,10 @@ void netpoll_poll_dev(struct net_device *dev)
if (!ni || down_trylock(&ni->dev_lock))
return;
if (!netif_running(dev)) {
/* Some drivers will take the same locks in poll and xmit,
* we can't poll if local CPU is already in xmit.
*/
if (!netif_running(dev) || netif_local_xmit_active(dev)) {
up(&ni->dev_lock);
return;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment