diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 8ecc2c71c699..918e4a9f4456 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -1654,6 +1654,23 @@ static struct notifier_block netconsole_netdev_notifier = { .notifier_call = netconsole_netdev_event, }; +/* Pop a pre-allocated skb from the pool and request a refill. + * + * The refill is requested via schedule_work(), which takes the workqueue + * pool locks and is therefore not NMI-safe. Skip the refill when called + * from NMI context; the next non-NMI caller will top the pool back up. + */ +static struct sk_buff *netcons_skb_pop(struct netpoll *np) +{ + struct sk_buff *skb; + + skb = skb_dequeue(&np->skb_pool); + if (!in_nmi()) + schedule_work(&np->refill_wq); + + return skb; +} + static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve) { int count = 0; @@ -1663,10 +1680,8 @@ static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve) repeat: skb = alloc_skb(len, GFP_ATOMIC); - if (!skb) { - skb = skb_dequeue(&np->skb_pool); - schedule_work(&np->refill_wq); - } + if (!skb) + skb = netcons_skb_pop(np); if (!skb) { if (++count < 10) {