netpoll holds the txq lock and calls ndo_start_xmit() with IRQs off but BHs on. The spin_unlock_bh() in cdc_ncm_tx_fixup() then runs pending softirqs. If one of them transmits, it spins on the txq lock that netpoll holds. With netconsole on a cdc_ncm device this ends in a soft or hard lockup. Use spin_lock_irqsave(), which is safe both under netpoll and on the normal xmit path. Fixes: c84ff1d6dff3 ("cdc_ncm: reduce driver latency in the data path") Reported-by: Mikkel Eskildsen Closes: https://lore.kernel.org/netdev/PRAP189MB185019A5A8D411482BB6E0CDA9982@PRAP189MB1850.EURP189.PROD.OUTLOOK.COM/ Tested-by: Mikkel Eskildsen Signed-off-by: Nicolai Buchwitz --- drivers/net/usb/cdc_ncm.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 35db38cb3e4a2..fed0b117a42f1 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1522,8 +1522,9 @@ static void cdc_ncm_txpath_bh(struct tasklet_struct *t) struct sk_buff * cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { - struct sk_buff *skb_out; struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + struct sk_buff *skb_out; + unsigned long irqflags; /* * The Ethernet API we are using does not support transmitting @@ -1535,14 +1536,17 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) if (ctx == NULL) goto error; - spin_lock_bh(&ctx->mtx); + /* netpoll calls this with IRQs off and BHs on, so unlocking with + * spin_unlock_bh() would run softirqs that need the held txq lock. + */ + spin_lock_irqsave(&ctx->mtx, irqflags); if (ctx->is_ndp16) skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); else skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN)); - spin_unlock_bh(&ctx->mtx); + spin_unlock_irqrestore(&ctx->mtx, irqflags); return skb_out; error: -- 2.53.0