* [PATCH] usbnet: fix smp_processor_id() use in preemptible context
@ 2026-09-03 16:54 Ömer Mete Kaya
2026-09-05 22:45 ` [PATCH net v2] " Ömer Mete Kaya
0 siblings, 1 reply; 10+ messages in thread
From: Ömer Mete Kaya @ 2026-09-03 16:54 UTC (permalink / raw)
To: netdev
Cc: oneukum, andrew+netdev, davem, edumazet, kuba, pabeni, linux-usb,
linux-kernel, Ömer Mete Kaya, syzbot+04cd90bb99c6ef81a65d
usbnet_skb_return() and tx_complete() call this_cpu_ptr() before
disabling IRQs, which triggers a BUG when running with PREEMPT_FULL:
BUG: using smp_processor_id() in preemptible code in tx_complete
Fix by saving IRQs first with local_irq_save(), then calling
this_cpu_ptr() and using the non-irqsave variants of u64_stats
update helpers, since IRQs are already disabled at that point.
Reported-by: syzbot+04cd90bb99c6ef81a65d@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=04cd90bb99c6ef81a65d
Signed-off-by: Ömer Mete Kaya <omermetekaya0@gmail.com>
---
Resend as standalone patch; previous send was incorrectly labeled [PATCH 1/2].
drivers/net/usb/usbnet.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index a19ecf718f36..6a48f38e105d 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -325,7 +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);
+ struct pcpu_sw_netstats *stats64;
unsigned long flags;
int status;
@@ -338,10 +338,13 @@ 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);
+ local_irq_save(flags);
+ stats64 = this_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);
+ local_irq_restore(flags);
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
skb->len + sizeof(struct ethhdr), skb->protocol);
@@ -1298,13 +1301,16 @@ 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);
+ struct pcpu_sw_netstats *stats64;
unsigned long flags;
- flags = u64_stats_update_begin_irqsave(&stats64->syncp);
+ local_irq_save(flags);
+ stats64 = this_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);
+ local_irq_restore(flags);
} else {
dev->net->stats.tx_errors++;
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH net v2] usbnet: fix smp_processor_id() use in preemptible context
2026-09-03 16:54 [PATCH] usbnet: fix smp_processor_id() use in preemptible context Ömer Mete Kaya
@ 2026-09-05 22:45 ` Ömer Mete Kaya
2026-09-07 11:38 ` Oliver Neukum
2026-09-08 3:43 ` [PATCH net v2] " netdev-bot+sashiko
0 siblings, 2 replies; 10+ messages in thread
From: Ömer Mete Kaya @ 2026-09-05 22:45 UTC (permalink / raw)
To: netdev
Cc: oneukum, andrew+netdev, davem, edumazet, kuba, pabeni, linux-usb,
linux-kernel, Ömer Mete Kaya, syzbot+04cd90bb99c6ef81a65d
usbnet_skb_return() and tx_complete() call this_cpu_ptr() before
disabling IRQs, which triggers a BUG when running with PREEMPT_FULL:
BUG: using smp_processor_id() in preemptible code in tx_complete
Fix by saving IRQs first with local_irq_save(), then calling
this_cpu_ptr() and using the non-irqsave variants of u64_stats
update helpers, since IRQs are already disabled at that point.
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 <omermetekaya0@gmail.com>
---
v2: Added Fixes: tag, changed prefix to [PATCH net].
drivers/net/usb/usbnet.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index a19ecf718f36..6a48f38e105d 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -325,7 +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);
+ struct pcpu_sw_netstats *stats64;
unsigned long flags;
int status;
@@ -338,10 +338,13 @@ 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);
+ local_irq_save(flags);
+ stats64 = this_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);
+ local_irq_restore(flags);
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
skb->len + sizeof(struct ethhdr), skb->protocol);
@@ -1298,13 +1301,16 @@ 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);
+ struct pcpu_sw_netstats *stats64;
unsigned long flags;
- flags = u64_stats_update_begin_irqsave(&stats64->syncp);
+ local_irq_save(flags);
+ stats64 = this_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);
+ local_irq_restore(flags);
} else {
dev->net->stats.tx_errors++;
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH net v2] usbnet: fix smp_processor_id() use in preemptible context
2026-09-05 22:45 ` [PATCH net v2] " Ömer Mete Kaya
@ 2026-09-07 11:38 ` Oliver Neukum
2026-09-07 18:52 ` Ömer Mete Kaya
2026-09-07 19:05 ` [PATCH] " Ömer Mete Kaya
2026-09-08 3:43 ` [PATCH net v2] " netdev-bot+sashiko
1 sibling, 2 replies; 10+ messages in thread
From: Oliver Neukum @ 2026-09-07 11:38 UTC (permalink / raw)
To: Ömer Mete Kaya, netdev
Cc: oneukum, andrew+netdev, davem, edumazet, kuba, pabeni, linux-usb,
linux-kernel, syzbot+04cd90bb99c6ef81a65d
On 06.09.26 00:45, Ömer Mete Kaya wrote:
> usbnet_skb_return() and tx_complete() call this_cpu_ptr() before
> disabling IRQs, which triggers a BUG when running with PREEMPT_FULL:
>
> BUG: using smp_processor_id() in preemptible code in tx_complete
>
> Fix by saving IRQs first with local_irq_save(), then calling
> this_cpu_ptr() and using the non-irqsave variants of u64_stats
> update helpers, since IRQs are already disabled at that point.
Hi,
thank you for the patch. However, if the problem is preemption,
when not simply preempt_disable()/peempt_enable()?
Why is it necessary to switch off interrupts?
Regards
Oliver
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v2] usbnet: fix smp_processor_id() use in preemptible context
2026-09-07 11:38 ` Oliver Neukum
@ 2026-09-07 18:52 ` Ömer Mete Kaya
2026-09-07 20:23 ` David Laight
2026-09-07 19:05 ` [PATCH] " Ömer Mete Kaya
1 sibling, 1 reply; 10+ messages in thread
From: Ömer Mete Kaya @ 2026-09-07 18:52 UTC (permalink / raw)
To: oneukum; +Cc: netdev, linux-usb, Ömer Mete Kaya
Hi Oliver,
Thank you for the review. You are right.
I used local_irq_save()/restore() thinking that softirq writers could
race on tstats. After checking the tstats update paths, I agreed that
preemption protection is sufficient for these accesses.
I will send v3 using preempt_disable()/preempt_enable() as suggested.
Best regards,
Ömer Mete Kaya
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] usbnet: fix smp_processor_id() use in preemptible context
2026-09-07 11:38 ` Oliver Neukum
2026-09-07 18:52 ` Ömer Mete Kaya
@ 2026-09-07 19:05 ` Ömer Mete Kaya
1 sibling, 0 replies; 10+ messages in thread
From: Ömer Mete Kaya @ 2026-09-07 19:05 UTC (permalink / raw)
To: netdev
Cc: oneukum, andrew+netdev, davem, edumazet, kuba, pabeni, linux-usb,
linux-kernel, Ömer Mete Kaya, syzbot+04cd90bb99c6ef81a65d
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 disabling preemption around the this_cpu_ptr() call and the
per-CPU statistics update.
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 <omermetekaya0@gmail.com>
---
v3: Use preempt_disable()/preempt_enable() instead of
local_irq_save()/restore() as suggested by Oliver Neukum.
drivers/net/usb/usbnet.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index a19ecf718f36..f1e5bc8bf932 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,13 @@ 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);
+ preempt_disable();
+ stats64 = this_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);
+ preempt_enable();
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
skb->len + sizeof(struct ethhdr), skb->protocol);
@@ -1298,13 +1300,15 @@ 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);
+ preempt_disable();
+ stats64 = this_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);
+ preempt_enable();
} else {
dev->net->stats.tx_errors++;
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH net v2] usbnet: fix smp_processor_id() use in preemptible context
2026-09-07 18:52 ` Ömer Mete Kaya
@ 2026-09-07 20:23 ` David Laight
2026-09-07 21:45 ` [PATCH net v4] " Ömer Mete Kaya
0 siblings, 1 reply; 10+ messages in thread
From: David Laight @ 2026-09-07 20:23 UTC (permalink / raw)
To: Ömer Mete Kaya; +Cc: oneukum, netdev, linux-usb
On Mon, 7 Sep 2026 21:52:20 +0300
Ömer Mete Kaya <omermetekaya0@gmail.com> wrote:
> Hi Oliver,
>
> Thank you for the review. You are right.
>
> I used local_irq_save()/restore() thinking that softirq writers could
> race on tstats. After checking the tstats update paths, I agreed that
> preemption protection is sufficient for these accesses.
>
>
> I will send v3 using preempt_disable()/preempt_enable() as suggested.
Shouldn't you use get_cpu_ptr() and put_cpu_ptr() ?
David
>
> Best regards,
>
> Ömer Mete Kaya
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH net v4] usbnet: fix smp_processor_id() use in preemptible context
2026-09-07 20:23 ` David Laight
@ 2026-09-07 21:45 ` Ömer Mete Kaya
2026-09-09 21:47 ` netdev-bot+sashiko
0 siblings, 1 reply; 10+ messages in thread
From: Ömer Mete Kaya @ 2026-09-07 21:45 UTC (permalink / raw)
To: netdev
Cc: oneukum, andrew+netdev, davem, edumazet, kuba, pabeni, linux-usb,
linux-kernel, david.laight.linux, Ömer Mete Kaya,
syzbot+04cd90bb99c6ef81a65d
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 <omermetekaya0@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH net v2] usbnet: fix smp_processor_id() use in preemptible context
2026-09-05 22:45 ` [PATCH net v2] " Ömer Mete Kaya
2026-09-07 11:38 ` Oliver Neukum
@ 2026-09-08 3:43 ` netdev-bot+sashiko
1 sibling, 0 replies; 10+ messages in thread
From: netdev-bot+sashiko @ 2026-09-08 3:43 UTC (permalink / raw)
To: omermetekaya0
Cc: netdev, oneukum, andrew+netdev, davem, edumazet, kuba, pabeni,
linux-usb, linux-kernel, syzbot+04cd90bb99c6ef81a65d
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 2 · Low: 0
- [Medium] The commit message's premise is only true for one of the two
changed sites, and the Fixes: tag is provably wrong.
- [Medium] The patch replaces the arch-tuned u64_stats irqsave helpers
with an unconditional local_irq_save()/local_irq_restore() pair around…
--
usbnet: fix smp_processor_id() use in preemptible context
This patch moves the this_cpu_ptr() lookup of dev->net->tstats inside a
local_irq_save()/local_irq_restore() region in usbnet_skb_return() and
tx_complete(), and switches the u64_stats writers from the irqsave
helpers to the plain begin/end variants.
Closes: https://syzkaller.appspot.com/bug?extid=04cd90bb99c6ef81a65d
Two questions on the changelog first:
> usbnet_skb_return() and tx_complete() call this_cpu_ptr() before
> disabling IRQs, which triggers a BUG when running with PREEMPT_FULL:
[Severity: Medium]
Is the "usbnet_skb_return()" half of that sentence reachable? Every
caller I can find already runs with preemption disabled.
rx_process() -> usbnet_skb_return() is only reached from usbnet_bh(),
which is either the dev->delay timer callback:
timer_setup(&dev->delay, usbnet_bh, 0);
or run from usbnet_bh_work() queued on the BH workqueue:
queue_work(system_bh_wq, &dev->bh_work);
and BH work items execute from workqueue_softirq_action(), i.e. softirq
context. The minidriver ->rx_fixup() call sites (asix_common.c,
cdc_ncm.c, rndis_host.c, cx82310_eth.c, lg-vl600.c, ...) are all invoked
from that same rx_process() context.
The one remaining caller explicitly disables BH around the loop:
local_bh_disable();
clear_bit(EVENT_RX_PAUSED, &dev->flags);
while ((skb = skb_dequeue(&dev->rxq_pause)) != NULL) {
usbnet_skb_return(dev, skb);
...
That leaves tx_complete(), which really can run in preemptible task
context when the HCD does not use HCD_BH and usb_hcd_giveback_urb()
completes "in the caller's context". Would it make sense to describe the
change as a tx_complete() fix, or is there an RX path that reaches
usbnet_skb_return() with preemption enabled?
> Fixes: 43daa96b166c ("usbnet: Stop RX Q on MTU change")
[Severity: Medium]
Does this Fixes: tag point at the right commit? 43daa96b166c is from
2016 and only added usbnet_pause_rx()/usbnet_resume_rx() calls to
usbnet_change_mtu() plus an EVENT_RX_PAUSED test in usbnet_bh(); it never
touched the stats code. git blame on the pre-patch tx_complete() block
shows:
af0c351cc34857 (Heiner Kallweit 2020-11-10) struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->net->tstats);
2695578b896aea (Eric Dumazet 2018-03-05) flags = u64_stats_update_begin_irqsave(&stats64->syncp);
Should the tag name af0c351cc34857 instead, so that stable backports land
in trees that actually contain the code being changed?
> diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> index a19ecf718f36a..6a48f38e105d4 100644
> --- a/drivers/net/usb/usbnet.c
> +++ b/drivers/net/usb/usbnet.c
> @@ -338,10 +338,13 @@ 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);
> + local_irq_save(flags);
> + stats64 = this_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);
> + local_irq_restore(flags);
[Severity: Medium]
Does this add a hardirq-off window on 64-bit where there was none? In
include/linux/u64_stats_sync.h the 64-bit branch is:
static inline void __u64_stats_update_begin(struct u64_stats_sync *syncp) { }
static inline void __u64_stats_update_end(struct u64_stats_sync *syncp) { }
static inline unsigned long __u64_stats_irqsave(void) { return 0; }
so u64_stats_update_begin_irqsave() masked nothing on 64-bit, while
local_irq_save() masks unconditionally. usbnet_skb_return() runs once
per received packet, so this looks like a new per-packet interrupts-off
region for all 64-bit builds, PREEMPT_RT included.
The changelog describes it as ordering only:
> Fix by saving IRQs first with local_irq_save(), then calling
> this_cpu_ptr() and using the non-irqsave variants of u64_stats
> update helpers, since IRQs are already disabled at that point.
Since only preemption/migration needs to be suppressed to make
this_cpu_ptr() legal, would get_cpu_ptr()/put_cpu_ptr() around the
existing u64_stats_update_begin_irqsave()/u64_stats_update_end_irqrestore()
pair work here? That keeps IRQ masking on 32-bit only, and matches the
core helper dev_sw_netstats_rx_add() in include/linux/netdevice.h:
struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
u64_stats_update_begin(&tstats->syncp);
usbnet_resume_rx() already uses local_bh_disable() for the same purpose,
which would be another option.
> @@ -1298,13 +1301,16 @@ 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);
> + struct pcpu_sw_netstats *stats64;
> unsigned long flags;
>
> - flags = u64_stats_update_begin_irqsave(&stats64->syncp);
> + local_irq_save(flags);
> + stats64 = this_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);
> + local_irq_restore(flags);
[Severity: Medium]
Same question for the TX completion path: this site is the one that can
genuinely be entered preemptibly, but would get_cpu_ptr()/put_cpu_ptr()
plus the existing irqsave helpers be enough, rather than disabling
interrupts on every successful TX completion on 64-bit?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260905224553.595766-1-omermetekaya0%40gmail.com
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v4] usbnet: fix smp_processor_id() use in preemptible context
2026-09-07 21:45 ` [PATCH net v4] " Ömer Mete Kaya
@ 2026-09-09 21:47 ` netdev-bot+sashiko
2026-09-10 9:01 ` David Laight
0 siblings, 1 reply; 10+ messages in thread
From: netdev-bot+sashiko @ 2026-09-09 21:47 UTC (permalink / raw)
To: omermetekaya0
Cc: netdev, oneukum, andrew+netdev, davem, edumazet, kuba, pabeni,
linux-usb, linux-kernel, david.laight.linux,
syzbot+04cd90bb99c6ef81a65d
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 0 · Low: 0
- [High] usbnet: dropping
u64_stats_update_begin_irqsave()/u64_stats_update_end_irqrestore() in…
--
usbnet: fix smp_processor_id() use in preemptible context
This changes usbnet_skb_return() and tx_complete() to fetch the per-CPU
pcpu_sw_netstats pointer with get_cpu_ptr()/put_cpu_ptr() instead of a bare
this_cpu_ptr(), addressing a smp_processor_id()-in-preemptible-code report
from syzbot. Along with the pointer change it also switches the seqcount
write helpers from the _irqsave variants to the plain ones.
Closes: https://syzkaller.appspot.com/bug?extid=04cd90bb99c6ef81a65d
> diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> index a19ecf718f36a..3df72b0c6bcfc 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);
[Severity: High]
Does dropping the _irqsave seqcount helpers here re-open the problem that
commit 2695578b896a ("net: usbnet: fix potential deadlock on 32bit hosts")
closed?
The reported splat is about the per-CPU pointer being taken outside of a
preempt-disabled section, which get_cpu_ptr() addresses. But get_cpu_ptr()
only disables preemption:
include/linux/percpu-defs.h
#define get_cpu_ptr(var) \
({ \
preempt_disable(); \
this_cpu_ptr(var); \
})
so nothing masks interrupts across the seqcount write section anymore.
usbnet_skb_return() runs from usbnet_bh_work()->rx_process() (and from
usbnet_resume_rx() in BH-disabled process context), i.e. with hardirqs
enabled. tx_complete() below is the urb->complete callback, and for host
controllers that do not set HCD_BH it is invoked straight from the
controller's hardirq handler:
drivers/usb/core/hcd.c:usb_hcd_giveback_urb() {
if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
__usb_hcd_giveback_urb(urb);
return;
}
}
drivers/usb/core/hcd.c:__usb_hcd_giveback_urb() {
kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
urb->complete(urb);
kcov_remote_stop_softirq();
}
There is no local_irq_save() around urb->complete(). In-tree only
ehci-hcd, xhci and dwc2 set HCD_BH, so on OHCI/UHCI/musb/max3421/sl811 and
friends tx_complete() is a hardirq-context writer.
Both sites write the same object, since struct pcpu_sw_netstats has a
single u64_stats_sync shared by the rx and tx counters:
include/linux/netdevice.h
struct pcpu_sw_netstats {
u64_stats_t rx_packets;
u64_stats_t rx_bytes;
u64_stats_t tx_packets;
u64_stats_t tx_bytes;
struct u64_stats_sync syncp;
};
On 32-bit, where u64_stats_sync is a real seqcount, can the following
nesting happen on one CPU?
usbnet_bh_work() -> rx_process() -> usbnet_skb_return()
u64_stats_update_begin() /* seq odd, rx_bytes half written */
<HCD hardirq>
usb_hcd_giveback_urb() -> __usb_hcd_giveback_urb() -> tx_complete()
u64_stats_update_begin() /* seq even again */
u64_stats_update_end() /* seq odd */
u64_stats_update_end() /* seq even */
A reader coming in via dev_get_tstats64()->dev_fetch_sw_netstats() (for
example "ip -s link show") would then see an even sequence in the middle of
the outer update and accept a torn 64-bit counter without
u64_stats_fetch_retry() firing. With lockdep on 32-bit, is this also the
"inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W}" report that 2695578b896a
removed?
This is the constraint documented in include/linux/u64_stats_sync.h:
* 3) Write side must use the _irqsave() variant if other writers, or a reader,
* can be invoked from an IRQ context. On 64bit systems this variant does not
* disable interrupts.
Since the _irqsave variant compiles away on 64-bit, would it work to keep
it and only change the pointer acquisition?
stats64 = get_cpu_ptr(dev->net->tstats);
flags = u64_stats_update_begin_irqsave(&stats64->syncp);
u64_stats_inc(&stats64->rx_packets);
u64_stats_add(&stats64->rx_bytes, skb->len);
u64_stats_update_end_irqrestore(&stats64->syncp, flags);
put_cpu_ptr(dev->net->tstats);
The changelog only mentions the get_cpu_ptr()/put_cpu_ptr() conversion and
says nothing about dropping the IRQ-safe helpers; could that be spelled out
if the removal is intentional?
>
> 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 {
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260907214727.692307-1-omermetekaya0%40gmail.com
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH net v4] usbnet: fix smp_processor_id() use in preemptible context
2026-09-09 21:47 ` netdev-bot+sashiko
@ 2026-09-10 9:01 ` David Laight
0 siblings, 0 replies; 10+ messages in thread
From: David Laight @ 2026-09-10 9:01 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: omermetekaya0, netdev, oneukum, andrew+netdev, davem, edumazet,
kuba, pabeni, linux-usb, linux-kernel,
syzbot+04cd90bb99c6ef81a65d
On Wed, 09 Sep 2026 21:47:34 +0000
netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider:
>
> Critical: 0 · High: 1 · Medium: 0 · Low: 0
>
> - [High] usbnet: dropping
> u64_stats_update_begin_irqsave()/u64_stats_update_end_irqrestore() in…
>
> --
>
> usbnet: fix smp_processor_id() use in preemptible context
>
> This changes usbnet_skb_return() and tx_complete() to fetch the per-CPU
> pcpu_sw_netstats pointer with get_cpu_ptr()/put_cpu_ptr() instead of a bare
> this_cpu_ptr(), addressing a smp_processor_id()-in-preemptible-code report
> from syzbot. Along with the pointer change it also switches the seqcount
> write helpers from the _irqsave variants to the plain ones.
>
> Closes: https://syzkaller.appspot.com/bug?extid=04cd90bb99c6ef81a65d
>
> > diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> > index a19ecf718f36a..3df72b0c6bcfc 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);
>
> [Severity: High]
>
> Does dropping the _irqsave seqcount helpers here re-open the problem that
> commit 2695578b896a ("net: usbnet: fix potential deadlock on 32bit hosts")
> closed?
Looks that way.
I think you need the following sequence:
local_irq_save(flags);
stats64 = this_cpu_ptr(...)
u64_stats_update_begin(...)
...
u64_stats_update_end(...);
local_irq_restore(flags);
David
>
> The reported splat is about the per-CPU pointer being taken outside of a
> preempt-disabled section, which get_cpu_ptr() addresses. But get_cpu_ptr()
> only disables preemption:
>
> include/linux/percpu-defs.h
> #define get_cpu_ptr(var) \
> ({ \
> preempt_disable(); \
> this_cpu_ptr(var); \
> })
>
> so nothing masks interrupts across the seqcount write section anymore.
>
> usbnet_skb_return() runs from usbnet_bh_work()->rx_process() (and from
> usbnet_resume_rx() in BH-disabled process context), i.e. with hardirqs
> enabled. tx_complete() below is the urb->complete callback, and for host
> controllers that do not set HCD_BH it is invoked straight from the
> controller's hardirq handler:
>
> drivers/usb/core/hcd.c:usb_hcd_giveback_urb() {
> if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
> __usb_hcd_giveback_urb(urb);
> return;
> }
> }
>
> drivers/usb/core/hcd.c:__usb_hcd_giveback_urb() {
> kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
> urb->complete(urb);
> kcov_remote_stop_softirq();
> }
>
> There is no local_irq_save() around urb->complete(). In-tree only
> ehci-hcd, xhci and dwc2 set HCD_BH, so on OHCI/UHCI/musb/max3421/sl811 and
> friends tx_complete() is a hardirq-context writer.
>
> Both sites write the same object, since struct pcpu_sw_netstats has a
> single u64_stats_sync shared by the rx and tx counters:
>
> include/linux/netdevice.h
> struct pcpu_sw_netstats {
> u64_stats_t rx_packets;
> u64_stats_t rx_bytes;
> u64_stats_t tx_packets;
> u64_stats_t tx_bytes;
> struct u64_stats_sync syncp;
> };
>
> On 32-bit, where u64_stats_sync is a real seqcount, can the following
> nesting happen on one CPU?
>
> usbnet_bh_work() -> rx_process() -> usbnet_skb_return()
> u64_stats_update_begin() /* seq odd, rx_bytes half written */
> <HCD hardirq>
> usb_hcd_giveback_urb() -> __usb_hcd_giveback_urb() -> tx_complete()
> u64_stats_update_begin() /* seq even again */
> u64_stats_update_end() /* seq odd */
> u64_stats_update_end() /* seq even */
>
> A reader coming in via dev_get_tstats64()->dev_fetch_sw_netstats() (for
> example "ip -s link show") would then see an even sequence in the middle of
> the outer update and accept a torn 64-bit counter without
> u64_stats_fetch_retry() firing. With lockdep on 32-bit, is this also the
> "inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W}" report that 2695578b896a
> removed?
>
> This is the constraint documented in include/linux/u64_stats_sync.h:
>
> * 3) Write side must use the _irqsave() variant if other writers, or a reader,
> * can be invoked from an IRQ context. On 64bit systems this variant does not
> * disable interrupts.
>
> Since the _irqsave variant compiles away on 64-bit, would it work to keep
> it and only change the pointer acquisition?
>
> stats64 = get_cpu_ptr(dev->net->tstats);
> flags = u64_stats_update_begin_irqsave(&stats64->syncp);
> u64_stats_inc(&stats64->rx_packets);
> u64_stats_add(&stats64->rx_bytes, skb->len);
> u64_stats_update_end_irqrestore(&stats64->syncp, flags);
> put_cpu_ptr(dev->net->tstats);
>
> The changelog only mentions the get_cpu_ptr()/put_cpu_ptr() conversion and
> says nothing about dropping the IRQ-safe helpers; could that be spelled out
> if the removal is intentional?
>
> >
> > 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 {
>
> [ ... ]
>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-09-10 9:01 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 16:54 [PATCH] usbnet: fix smp_processor_id() use in preemptible context Ömer Mete Kaya
2026-09-05 22:45 ` [PATCH net v2] " Ömer Mete Kaya
2026-09-07 11:38 ` Oliver Neukum
2026-09-07 18:52 ` Ömer Mete Kaya
2026-09-07 20:23 ` David Laight
2026-09-07 21:45 ` [PATCH net v4] " Ömer Mete Kaya
2026-09-09 21:47 ` netdev-bot+sashiko
2026-09-10 9:01 ` David Laight
2026-09-07 19:05 ` [PATCH] " Ömer Mete Kaya
2026-09-08 3:43 ` [PATCH net v2] " netdev-bot+sashiko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox