usbnet_skb_return() and tx_complete() call this_cpu_ptr() before disabling preemption, which triggers a BUG when running with PREEMPT_FULL: BUG: using smp_processor_id() in preemptible code in tx_complete Fix by using get_cpu_ptr()/put_cpu_ptr() which disable preemption and return the per-CPU pointer atomically. Fixes: 43daa96b166c ("usbnet: Stop RX Q on MTU change") Reported-by: syzbot+04cd90bb99c6ef81a65d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=04cd90bb99c6ef81a65d Signed-off-by: Ă–mer Mete Kaya --- v4: Use get_cpu_ptr()/put_cpu_ptr() as suggested by David Laight. drivers/net/usb/usbnet.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index a19ecf718f36..3df72b0c6bcf 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -325,8 +325,7 @@ static void __usbnet_status_stop_force(struct usbnet *dev) */ void usbnet_skb_return(struct usbnet *dev, struct sk_buff *skb) { - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->net->tstats); - unsigned long flags; + struct pcpu_sw_netstats *stats64; int status; if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { @@ -338,10 +337,12 @@ void usbnet_skb_return(struct usbnet *dev, struct sk_buff *skb) if (skb->protocol == 0) skb->protocol = eth_type_trans(skb, dev->net); - flags = u64_stats_update_begin_irqsave(&stats64->syncp); + stats64 = get_cpu_ptr(dev->net->tstats); + u64_stats_update_begin(&stats64->syncp); u64_stats_inc(&stats64->rx_packets); u64_stats_add(&stats64->rx_bytes, skb->len); - u64_stats_update_end_irqrestore(&stats64->syncp, flags); + u64_stats_update_end(&stats64->syncp); + put_cpu_ptr(dev->net->tstats); netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n", skb->len + sizeof(struct ethhdr), skb->protocol); @@ -1298,13 +1299,14 @@ static void tx_complete(struct urb *urb) struct usbnet *dev = entry->dev; if (urb->status == 0) { - struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->net->tstats); - unsigned long flags; + struct pcpu_sw_netstats *stats64; - flags = u64_stats_update_begin_irqsave(&stats64->syncp); + stats64 = get_cpu_ptr(dev->net->tstats); + u64_stats_update_begin(&stats64->syncp); u64_stats_add(&stats64->tx_packets, entry->packets); u64_stats_add(&stats64->tx_bytes, entry->length); - u64_stats_update_end_irqrestore(&stats64->syncp, flags); + u64_stats_update_end(&stats64->syncp); + put_cpu_ptr(dev->net->tstats); } else { dev->net->stats.tx_errors++; -- 2.55.0