mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
@ 2026-08-24 10:56 Sunho Park
  2026-08-25 12:33 ` Zqiang
  0 siblings, 1 reply; 14+ messages in thread
From: Sunho Park @ 2026-08-24 10:56 UTC (permalink / raw)
  To: rcu
  Cc: paulmck, qiang.zhang, linux-kernel, Sunho Park,
	syzbot+d4faf7db59e11f6fd1ab

The main crash report [1] which is tested on non-merged commit 6b8c8af514d7
is caused by the single-condition WARN_ON(timer_delete_sync(&sdp->delay_work))
in cleanup_srcu_struct(&kvm->irq_srcu). As discussed in [2], it is a false
positive because irq_srcu does not use call_srcu().

However, the merged WARN_ON(timer_delete_sync(&sdp->delay_work) &&
rcu_segcblist_n_cbs(&sdp->srcu_cblist)) is also triggered in
cleanup_srcu_struct(&kvm->srcu) which is called after srcu_barrier() properly.
Although my syz test command [3] failed to reproduce, it was reproducible
in my QEMU environment built with the .config of the report.

I found out that the return value of rcu_segcblist_n_cbs can be nonzero
even after srcu_barrier() because of the srcu_barrier_cb() that srcu_barrier()
inserts at the end of the queue. The length of cblist is decreased after
srcu_invoke_callbacks() finishes invoking all callbacks in a batch. But
srcu_barrier() may return when all the srcu_barrier_cb() are called, bringing
the counter to zero, even if srcu_invoke_callbacks() has not yet decremented
the length. So checking cblist length before flush_work() is inaccurate.

By the comment of srcu_barrier(), it guarantees that all the previously
registered call_srcu() callbacks are completed. Therefore srcu_barrier()
did what it said, only the length of cblist was not updated. I think there
are two options:

1) Not to check the length of cblist before flush_work()
2) Make srcu_barrier() guarantee the length of cblist is adjusted when it returns

[1] https://lore.kernel.org/all/6a78d191.b50370da.49fe0.0042.GAE@google.com/T
[2] https://lore.kernel.org/rcu/01484cd339ea024dd7c01f02a9781f00e67cb52d@linux.dev/T
[3] https://lore.kernel.org/all/6a8bc111.91706f20.16b6e3.02cd.GAE@google.com

Reported-by: syzbot+d4faf7db59e11f6fd1ab@syzkaller.appspotmail.com

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after  78a38cbf6f20
  2026-08-24 10:56 [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20 Sunho Park
@ 2026-08-25 12:33 ` Zqiang
  2026-08-25 16:50   ` Sunho Park
  0 siblings, 1 reply; 14+ messages in thread
From: Zqiang @ 2026-08-25 12:33 UTC (permalink / raw)
  To: Sunho Park, rcu
  Cc: paulmck, linux-kernel, Sunho Park, syzbot+d4faf7db59e11f6fd1ab

> 
> The main crash report [1] which is tested on non-merged commit 6b8c8af514d7
> is caused by the single-condition WARN_ON(timer_delete_sync(&sdp->delay_work))
> in cleanup_srcu_struct(&kvm->irq_srcu). As discussed in [2], it is a false
> positive because irq_srcu does not use call_srcu().
> 
> However, the merged WARN_ON(timer_delete_sync(&sdp->delay_work) &&
> rcu_segcblist_n_cbs(&sdp->srcu_cblist)) is also triggered in
> cleanup_srcu_struct(&kvm->srcu) which is called after srcu_barrier() properly.
> Although my syz test command [3] failed to reproduce, it was reproducible
> in my QEMU environment built with the .config of the report.
> 
> I found out that the return value of rcu_segcblist_n_cbs can be nonzero
> even after srcu_barrier() because of the srcu_barrier_cb() that srcu_barrier()
> inserts at the end of the queue. The length of cblist is decreased after
> srcu_invoke_callbacks() finishes invoking all callbacks in a batch. But
> srcu_barrier() may return when all the srcu_barrier_cb() are called, bringing
> the counter to zero, even if srcu_invoke_callbacks() has not yet decremented
> the length. So checking cblist length before flush_work() is inaccurate.

If srcu_barrier() be invoke before srcu_cleanup(), and after srcu_barrier()
completion, there are no concurrent srcu grace period start again (e.g. call_srcu() calls), 
the timer_delete_sync() should return false, the rcu_segcblist_n_cbs()
will not be check.

Or did I miss something?

Thanks
Zqiang


> 
> By the comment of srcu_barrier(), it guarantees that all the previously
> registered call_srcu() callbacks are completed. Therefore srcu_barrier()
> did what it said, only the length of cblist was not updated. I think there
> are two options:
> 
> 1) Not to check the length of cblist before flush_work()
> 2) Make srcu_barrier() guarantee the length of cblist is adjusted when it returns
> 
> [1] https://lore.kernel.org/all/6a78d191.b50370da.49fe0.0042.GAE@google.com/T
> [2] https://lore.kernel.org/rcu/01484cd339ea024dd7c01f02a9781f00e67cb52d@linux.dev/T
> [3] https://lore.kernel.org/all/6a8bc111.91706f20.16b6e3.02cd.GAE@google.com
> 
> Reported-by: syzbot+d4faf7db59e11f6fd1ab@syzkaller.appspotmail.com
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-25 12:33 ` Zqiang
@ 2026-08-25 16:50   ` Sunho Park
  2026-08-26 13:13     ` Zqiang
  0 siblings, 1 reply; 14+ messages in thread
From: Sunho Park @ 2026-08-25 16:50 UTC (permalink / raw)
  To: Zqiang, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

On 8/25/26 21:33, Zqiang wrote:
> 
> If srcu_barrier() be invoke before srcu_cleanup(), and after srcu_barrier()
> completion, there are no concurrent srcu grace period start again (e.g. call_srcu() calls),
> the timer_delete_sync() should return false, the rcu_segcblist_n_cbs()
> will not be check.
> 
> Or did I miss something?
In srcu_gp_end(), delayed work timer is always armed in SRCU_SIZE_SMALL 
mode regardless of whether its cblist has callbacks:

	if (ss_state < SRCU_SIZE_WAIT_BARRIER) {
		srcu_schedule_cbs_sdp(per_cpu_ptr(ssp->sda, get_boot_cpu_id()),
					cbdelay);
	} else {
		...
	}

and with cbdelay=1 (the non-expedited path) srcu_schedule_cbs_sdp() arms 
the per-CPU delay_work timer for the next jiffy. This includes the 
srcu_gp_end() whose grace period makes the barrier's callbacks ready, so 
an armed timer is left pending when srcu_invoke_callbacks() completes 
the barrier.

Thanks
Sunho Park

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-25 16:50   ` Sunho Park
@ 2026-08-26 13:13     ` Zqiang
  2026-08-26 16:03       ` Sunho Park
  0 siblings, 1 reply; 14+ messages in thread
From: Zqiang @ 2026-08-26 13:13 UTC (permalink / raw)
  To: Sunho Park, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

> 
> On 8/25/26 21:33, Zqiang wrote:
> 
> > 
> > If srcu_barrier() be invoke before srcu_cleanup(), and after srcu_barrier()
> >  completion, there are no concurrent srcu grace period start again (e.g. call_srcu() calls),
> >  the timer_delete_sync() should return false, the rcu_segcblist_n_cbs()
> >  will not be check.
> >  Or did I miss something?
> > 
> In srcu_gp_end(), delayed work timer is always armed in SRCU_SIZE_SMALL mode regardless of whether its cblist has callbacks:

In this SRCU_SIZE_SMALL mode, when queue delayed work timer, the cblist is always no-empty.
unless invoke start_poll_synchronize_srcu() to begin SRCU garce period, 
otherwise invoke call_srcu() or synchronize_srcu*() will insert callback.

Thanks
Zqiang

> 
>  if (ss_state < SRCU_SIZE_WAIT_BARRIER) {
>  srcu_schedule_cbs_sdp(per_cpu_ptr(ssp->sda, get_boot_cpu_id()),
>  cbdelay);
>  } else {
>  ...
>  }
> 
> and with cbdelay=1 (the non-expedited path) srcu_schedule_cbs_sdp() arms the per-CPU delay_work timer for the next jiffy. This includes the srcu_gp_end() whose grace period makes the barrier's callbacks ready, so an armed timer is left pending when srcu_invoke_callbacks() completes the barrier.
> 
> Thanks
> Sunho Park
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-26 13:13     ` Zqiang
@ 2026-08-26 16:03       ` Sunho Park
  2026-08-26 23:53         ` Zqiang
  0 siblings, 1 reply; 14+ messages in thread
From: Sunho Park @ 2026-08-26 16:03 UTC (permalink / raw)
  To: Zqiang, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

On 8/26/26 22:13, Zqiang wrote:
> In this SRCU_SIZE_SMALL mode, when queue delayed work timer, the cblist is always no-empty.
> unless invoke start_poll_synchronize_srcu() to begin SRCU garce period,
> otherwise invoke call_srcu() or synchronize_srcu*() will insert callback.
Right, every grace period which armed the delay_work timer is started by 
the real call_srcu(&kvm->srcu, &bus->rcu, __free_bus) in 
kvm_io_bus_register_dev(). I missed one point: the invoke work which 
invoked the barrier callbacks is not queued by the nearest end of grace 
period. It was queued by the timer of a previous end of GP, and it 
started only after the last GP had ended. Meanwhile the srcu_gp_end() 
from the last GP armed another timer even though the work was already 
queued. The timeline is as below:

1. call_srcu(&kvm->srcu, &bus->rcu, __free_bus)
2. One end of GP comes, arms a timer.
3. The timer is fired and an invoke work is queued to rcu_gp_wq. The 
timer is disabled now.
4. Another call_srcu(&kvm->srcu, &bus->rcu, __free_bus)
5. srcu_barrier() is called and queues barrier callbacks, waits for 
srcu_invoke_callbacks() to invoke them.
6. The end of GP from step 4 comes, arms another timer.
7. The invoke work queued in step 3 starts, srcu_invoke_callbacks() is 
called. It starts invoking callbacks without subtracting the cblist len 
field. It will call rcu_segcblist_add_len(&sdp->srcu_cblist, -len) after 
the invoking loop is over.
When I debugged, there were five __free_bus(the real callbacks) and one 
barrier callback, so the cblist len field was 6.
8. Barrier callback is invoked, still the cblist len field is not 
subtracted as srcu_invoke_callbacks()'s invoking loop is not over.
9. srcu_barrier() wakes up by completion and cleanup_srcu_struct() is 
called before the timer armed in step 6 expires.
At this point the cblist is physically empty (head == NULL, all seglen 
are 0) as all six callbacks have already been invoked. Only the cblist 
len field is stale(>0).

Thanks
Sunho Park

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-26 16:03       ` Sunho Park
@ 2026-08-26 23:53         ` Zqiang
  2026-08-27  9:11           ` Sunho Park
  0 siblings, 1 reply; 14+ messages in thread
From: Zqiang @ 2026-08-26 23:53 UTC (permalink / raw)
  To: Sunho Park, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

> 
> On 8/26/26 22:13, Zqiang wrote:
> 
> > 
> > In this SRCU_SIZE_SMALL mode, when queue delayed work timer, the cblist is always no-empty.
> >  unless invoke start_poll_synchronize_srcu() to begin SRCU garce period,
> >  otherwise invoke call_srcu() or synchronize_srcu*() will insert callback.
> > 
> Right, every grace period which armed the delay_work timer is started by the real call_srcu(&kvm->srcu, &bus->rcu, __free_bus) in kvm_io_bus_register_dev(). I missed one point: the invoke work which invoked the barrier callbacks is not queued by the nearest end of grace period. It was queued by the timer of a previous end of GP, and it started only after the last GP had ended. Meanwhile the srcu_gp_end() from the last GP armed another timer even though the work was already queued. The timeline is as below:
> 
> 1. call_srcu(&kvm->srcu, &bus->rcu, __free_bus)
> 2. One end of GP comes, arms a timer.
> 3. The timer is fired and an invoke work is queued to rcu_gp_wq. The timer is disabled now.
> 4. Another call_srcu(&kvm->srcu, &bus->rcu, __free_bus)
> 5. srcu_barrier() is called and queues barrier callbacks, waits for srcu_invoke_callbacks() to invoke them.

Based on your description below, the 5.srcu_barrier() did not intercept the callback of 4.call_srcu(),
this means that 4.call_srcu() and 5.srcu_barrier() concurrent calls, or calls after 5.srcu_barrier().

The combination of srcu_barrier() and cleanup_srcu_struct() typically occurs on the module exit path.
srcu_barrier() ensures that all previously inserted callbacks complete,so therefore, theoretically,
there shouldn't be any callbacks left to execute after we finish executing srcu_barrier(). 
and of course, there are also shouldn't be any ongoing or newly started SRCU grace period and the
WARN_ON() in cleanup_srcu_struct() is designed to detect it.

If when the cleanup_srcu_struct() detects an incomplete SRCU grace period or any srcu callbacks
that have not yet been executed, this is a risk that needs to be reported, even if the
cleanup_srcu_struct() can intercept it internally.
 
Therefore, we should investigate this issue to ensure that 5.srcu_barrier() can intercept the step 4 callback.

Thanks
Zqiang


> 6. The end of GP from step 4 comes, arms another timer.
> 7. The invoke work queued in step 3 starts, srcu_invoke_callbacks() is called. It starts invoking callbacks without subtracting the cblist len field. It will call rcu_segcblist_add_len(&sdp->srcu_cblist, -len) after the invoking loop is over.
> When I debugged, there were five __free_bus(the real callbacks) and one barrier callback, so the cblist len field was 6.
> 8. Barrier callback is invoked, still the cblist len field is not subtracted as srcu_invoke_callbacks()'s invoking loop is not over.
> 9. srcu_barrier() wakes up by completion and cleanup_srcu_struct() is called before the timer armed in step 6 expires.
> At this point the cblist is physically empty (head == NULL, all seglen are 0) as all six callbacks have already been invoked. Only the cblist len field is stale(>0).
> 
> Thanks
> Sunho Park
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-26 23:53         ` Zqiang
@ 2026-08-27  9:11           ` Sunho Park
  2026-08-27 11:13             ` Zqiang
  0 siblings, 1 reply; 14+ messages in thread
From: Sunho Park @ 2026-08-27  9:11 UTC (permalink / raw)
  To: Zqiang, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

On 8/27/26 08:53, Zqiang wrote:
> Based on your description below, the 5.srcu_barrier() did not intercept the callback of 4.call_srcu(),
> this means that 4.call_srcu() and 5.srcu_barrier() concurrent calls, or calls after 5.srcu_barrier().
> 
> The combination of srcu_barrier() and cleanup_srcu_struct() typically occurs on the module exit path.
> srcu_barrier() ensures that all previously inserted callbacks complete,so therefore, theoretically,
> there shouldn't be any callbacks left to execute after we finish executing srcu_barrier().
> and of course, there are also shouldn't be any ongoing or newly started SRCU grace period and the
> WARN_ON() in cleanup_srcu_struct() is designed to detect it.
> 
> If when the cleanup_srcu_struct() detects an incomplete SRCU grace period or any srcu callbacks
> that have not yet been executed, this is a risk that needs to be reported, even if the
> cleanup_srcu_struct() can intercept it internally.
>   
> Therefore, we should investigate this issue to ensure that 5.srcu_barrier() can intercept the step 4 callback.
> 
> Thanks
> Zqiang

Before 5. srcu_barrier() call, the 4. call_srcu() is finished and the 
callback was enqueued, and srcu_barrier() did intercept it by appending 
barrier cb after it. They can't be called concurrently because when 
call_srcu() is called inside kvm_io_bus_register_dev(), the kvm 
reference count is nonzero and kvm_destroy_vm() which calls 
srcu_barrier() could not be started.

In my debugging, all five __free_bus() callbacks enqueued by 
kvm_io_bus_register_dev() were invoked before the barrier cb, and only 
then the barrier cb is called and srcu_barrier() returns. So all the 
real callbacks were executed and practically there were no callbacks 
left to execute when srcu_barrier() returns, but a stale n_cbs > 0 is 
left because srcu_invoke_callbacks()'s invoking loop has not finished.

KVM logic correctly called srcu_barrier() and cleanup_srcu_struct() 
without calling call_srcu() in between. In my opinion, the root causes 
are as follows:

1) for timer_delete_sync(): the srcu_gp_end() which ended the last grace 
period (from 4. ) arms sdp->delay_work to expire at jiffies + 1 even 
though the invoke work (queued in 3. ) is already queued, and 
cleanup_srcu_struct() runs before that one-jiffy timer expires, so 
timer_delete_sync() cancels that pending timer and returns true.

2) for n_cbs: srcu_invoke_callbacks() calls rcu_segcblist_add_len(-len) 
only at the end of the work item, after the barrier callback has been 
invoked and srcu_barrier() has already returned, so 
cleanup_srcu_struct() observes a stale n_cbs > 0 while the cblist is 
physically empty.

Thanks
Sunho Park

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-27  9:11           ` Sunho Park
@ 2026-08-27 11:13             ` Zqiang
  2026-08-27 11:35               ` Zqiang
  0 siblings, 1 reply; 14+ messages in thread
From: Zqiang @ 2026-08-27 11:13 UTC (permalink / raw)
  To: Sunho Park, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

> 
> On 8/27/26 08:53, Zqiang wrote:
> 
> > 
> > Based on your description below, the 5.srcu_barrier() did not intercept the callback of 4.call_srcu(),
> >  this means that 4.call_srcu() and 5.srcu_barrier() concurrent calls, or calls after 5.srcu_barrier().
> >  The combination of srcu_barrier() and cleanup_srcu_struct() typically occurs on the module exit path.
> >  srcu_barrier() ensures that all previously inserted callbacks complete,so therefore, theoretically,
> >  there shouldn't be any callbacks left to execute after we finish executing srcu_barrier().
> >  and of course, there are also shouldn't be any ongoing or newly started SRCU grace period and the
> >  WARN_ON() in cleanup_srcu_struct() is designed to detect it.
> >  If when the cleanup_srcu_struct() detects an incomplete SRCU grace period or any srcu callbacks
> >  that have not yet been executed, this is a risk that needs to be reported, even if the
> >  cleanup_srcu_struct() can intercept it internally.
> >  > Therefore, we should investigate this issue to ensure that 5.srcu_barrier() can intercept the step 4 callback.
> >  Thanks
> >  Zqiang
> > 
> Before 5. srcu_barrier() call, the 4. call_srcu() is finished and the callback was enqueued, and srcu_barrier() did intercept it by appending barrier cb after it. They can't be called concurrently because when call_srcu() is called inside kvm_io_bus_register_dev(), the kvm reference count is nonzero and kvm_destroy_vm() which calls srcu_barrier() could not be started.
>

Which tree is your test based on? (rcu tree or linux-next tree)

The step 4 call_srcu() is finished and then the step5 call srcu_barrier(), it failed to intercept it?
(theoretically, this shouldn't happen)

 
> In my debugging, all five __free_bus() callbacks enqueued by kvm_io_bus_register_dev() were invoked before the barrier cb, and only then the barrier cb is called and srcu_barrier() returns. So all the real callbacks were executed and practically there were no callbacks left to execute when srcu_barrier() returns, but a stale n_cbs > 0 is left because srcu_invoke_callbacks()'s invoking loop has not finished.
> 
> KVM logic correctly called srcu_barrier() and cleanup_srcu_struct() without calling call_srcu() in between. In my opinion, the root causes are as follows:
> 
> 1) for timer_delete_sync(): the srcu_gp_end() which ended the last grace period (from 4. ) arms sdp->delay_work to expire at jiffies + 1 even though the invoke work (queued in 3. ) is already queued, and cleanup_srcu_struct() runs before that one-jiffy timer expires, so timer_delete_sync() cancels that pending timer and returns true.
> 
> 2) for n_cbs: srcu_invoke_callbacks() calls rcu_segcblist_add_len(-len) only at the end of the work item, after the barrier callback has been invoked and srcu_barrier() has already returned, so cleanup_srcu_struct() observes a stale n_cbs > 0 while the cblist is physically empty.

because you say the 5.srcu_barrier() can not intercept setp4 call_srcu() enqueue callback func, 
that means that the step4 srcu callback shuold not be run, the cblist also not empty.

Thanks
Zqiang 

> 
> Thanks
> Sunho Park
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-27 11:13             ` Zqiang
@ 2026-08-27 11:35               ` Zqiang
  2026-08-27 12:30                 ` Sunho Park
  2026-08-27 12:40                 ` Zqiang
  0 siblings, 2 replies; 14+ messages in thread
From: Zqiang @ 2026-08-27 11:35 UTC (permalink / raw)
  To: Sunho Park, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

> 
> > 
> > On 8/27/26 08:53, Zqiang wrote:
> >  
> >  
> >  Based on your description below, the 5.srcu_barrier() did not intercept the callback of 4.call_srcu(),
> >  this means that 4.call_srcu() and 5.srcu_barrier() concurrent calls, or calls after 5.srcu_barrier().
> >  The combination of srcu_barrier() and cleanup_srcu_struct() typically occurs on the module exit path.
> >  srcu_barrier() ensures that all previously inserted callbacks complete,so therefore, theoretically,
> >  there shouldn't be any callbacks left to execute after we finish executing srcu_barrier().
> >  and of course, there are also shouldn't be any ongoing or newly started SRCU grace period and the
> >  WARN_ON() in cleanup_srcu_struct() is designed to detect it.
> >  If when the cleanup_srcu_struct() detects an incomplete SRCU grace period or any srcu callbacks
> >  that have not yet been executed, this is a risk that needs to be reported, even if the
> >  cleanup_srcu_struct() can intercept it internally.
> >  > Therefore, we should investigate this issue to ensure that 5.srcu_barrier() can intercept the step 4 callback.
> >  Thanks
> >  Zqiang
> >  
> >  Before 5. srcu_barrier() call, the 4. call_srcu() is finished and the callback was enqueued, and srcu_barrier() did intercept it by appending barrier cb after it. They can't be called concurrently because when call_srcu() is called inside kvm_io_bus_register_dev(), the kvm reference count is nonzero and kvm_destroy_vm() which calls srcu_barrier() could not be started.
> > 
> Which tree is your test based on? (rcu tree or linux-next tree)
> 
> The step 4 call_srcu() is finished and then the step5 call srcu_barrier(), it failed to intercept it?
> (theoretically, this shouldn't happen)
> 

Sorry, here I missed.

>  
> 
> > 
> > In my debugging, all five __free_bus() callbacks enqueued by kvm_io_bus_register_dev() were invoked before the barrier cb, and only then the barrier cb is called and srcu_barrier() returns. So all the real callbacks were executed and practically there were no callbacks left to execute when srcu_barrier() returns, but a stale n_cbs > 0 is left because srcu_invoke_callbacks()'s invoking loop has not finished.
> >  
> >  KVM logic correctly called srcu_barrier() and cleanup_srcu_struct() without calling call_srcu() in between. In my opinion, the root causes are as follows:
> >  
> >  1) for timer_delete_sync(): the srcu_gp_end() which ended the last grace period (from 4. ) arms sdp->delay_work to expire at jiffies + 1 even though the invoke work (queued in 3. ) is already queued, and cleanup_srcu_struct() runs before that one-jiffy timer expires, so timer_delete_sync() cancels that pending timer and returns true.

The step5 srcu_barrier() has been intercept the srcu callback which by step4 call_srcu() insert,
so at here, the srcu_barrier should not be return, and the step4 srcu callback should not be run.
the cleanup_srcu_struct() also should not be called.


Thanks
Zqiang


> >  
> >  2) for n_cbs: srcu_invoke_callbacks() calls rcu_segcblist_add_len(-len) only at the end of the work item, after the barrier callback has been invoked and srcu_barrier() has already returned, so cleanup_srcu_struct() observes a stale n_cbs > 0 while the cblist is physically empty.
> > 
> because you say the 5.srcu_barrier() can not intercept setp4 call_srcu() enqueue callback func, 
> that means that the step4 srcu callback shuold not be run, the cblist also not empty.
> 
> Thanks
> Zqiang 
> 
> > 
> > Thanks
> >  Sunho Park
> >
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-27 11:35               ` Zqiang
@ 2026-08-27 12:30                 ` Sunho Park
  2026-08-27 12:40                 ` Zqiang
  1 sibling, 0 replies; 14+ messages in thread
From: Sunho Park @ 2026-08-27 12:30 UTC (permalink / raw)
  To: Zqiang, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

On 8/27/26 20:35, Zqiang wrote:
>> Which tree is your test based on? (rcu tree or linux-next tree)
I'm testing on the linux-next tree(next-20260821), regarding the commit 
[1].
> The step5 srcu_barrier() has been intercept the srcu callback which by step4 call_srcu() insert,
> so at here, the srcu_barrier should not be return, and the step4 srcu callback should not be run.
> the cleanup_srcu_struct() also should not be called.
srcu_barrier() didn't return at that point, it returned at step 9 when 
srcu_invoke_callbacks() (which was queued to rcu_gp_wq at step 3) calls 
the barrier cb after finishing the real five __free_bus callbacks. Steps 
3 through 9 all happen within the same jiffy, so the timer armed at step 
6 for the next jiffy is still pending at step 9. Also, a stale n_cbs is 
left because, although srcu_invoke_callbacks()'s invoking loop may have 
finished at that point, the control flow has not yet reached the 
rcu_segcblist_add_len(-len) call at the end of srcu_invoke_callbacks().

[1] 
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/kernel/rcu/srcutree.c?id=78a38cbf6f20bc8247e93d1149f97c12dba9fbfb

Thanks
Sunho Park

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-27 11:35               ` Zqiang
  2026-08-27 12:30                 ` Sunho Park
@ 2026-08-27 12:40                 ` Zqiang
  2026-08-27 13:03                   ` Sunho Park
  1 sibling, 1 reply; 14+ messages in thread
From: Zqiang @ 2026-08-27 12:40 UTC (permalink / raw)
  To: Sunho Park, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

> 
> > 
> > On 8/27/26 08:53, Zqiang wrote:
> >  
> >  
> >  Based on your description below, the 5.srcu_barrier() did not intercept the callback of 4.call_srcu(),
> >  this means that 4.call_srcu() and 5.srcu_barrier() concurrent calls, or calls after 5.srcu_barrier().
> >  The combination of srcu_barrier() and cleanup_srcu_struct() typically occurs on the module exit path.
> >  srcu_barrier() ensures that all previously inserted callbacks complete,so therefore, theoretically,
> >  there shouldn't be any callbacks left to execute after we finish executing srcu_barrier().
> >  and of course, there are also shouldn't be any ongoing or newly started SRCU grace period and the
> >  WARN_ON() in cleanup_srcu_struct() is designed to detect it.
> >  If when the cleanup_srcu_struct() detects an incomplete SRCU grace period or any srcu callbacks
> >  that have not yet been executed, this is a risk that needs to be reported, even if the
> >  cleanup_srcu_struct() can intercept it internally.
> >  > Therefore, we should investigate this issue to ensure that 5.srcu_barrier() can intercept the step 4 callback.
> >  Thanks
> >  Zqiang
> >  
> >  Before 5. srcu_barrier() call, the 4. call_srcu() is finished and the callback was enqueued, and srcu_barrier() did intercept it by appending barrier cb after it. They can't be called concurrently because when call_srcu() is called inside kvm_io_bus_register_dev(), the kvm reference count is nonzero and kvm_destroy_vm() which calls srcu_barrier() could not be started.
> >  
> >  Which tree is your test based on? (rcu tree or linux-next tree)
> >  
> >  The step 4 call_srcu() is finished and then the step5 call srcu_barrier(), it failed to intercept it?
> >  (theoretically, this shouldn't happen)
> > 
> Sorry, here I missed.
> 
> > 
> > In my debugging, all five __free_bus() callbacks enqueued by kvm_io_bus_register_dev() were invoked before the barrier cb, and only then the barrier cb is called and srcu_barrier() returns. So all the real callbacks were executed and practically there were no callbacks left to execute when srcu_barrier() returns, but a stale n_cbs > 0 is left because srcu_invoke_callbacks()'s invoking loop has not finished.
> >  
> >  KVM logic correctly called srcu_barrier() and cleanup_srcu_struct() without calling call_srcu() in between. In my opinion, the root causes are as follows:
> >  
> >  1) for timer_delete_sync(): the srcu_gp_end() which ended the last grace period (from 4. ) arms sdp->delay_work to expire at jiffies + 1 even though the invoke work (queued in 3. ) is already queued, and cleanup_srcu_struct() runs before that one-jiffy timer expires, so timer_delete_sync() cancels that pending timer and returns true.
> > 
> The step5 srcu_barrier() has been intercept the srcu callback which by step4 call_srcu() insert,
> so at here, the srcu_barrier should not be return, and the step4 srcu callback should not be run.
> the cleanup_srcu_struct() also should not be called.
> 
> Thanks
> Zqiang
> 
> > 
> > 2) for n_cbs: srcu_invoke_callbacks() calls rcu_segcblist_add_len(-len) only at the end of the work item, after the barrier callback has been invoked and srcu_barrier() has already returned, so cleanup_srcu_struct() observes a stale n_cbs > 0 while the cblist is physically empty.



Now, I am trying to rephrase your problem in the following way:

1- the step1 call_srcu() trigger SRCU grace period has been end and queue sdp->work.
2- the step4 queue srcu callback has been intercepted by step5 srcu_barrier()
   and start a new SRCU grace period.
3- this new SRCU grace period end, and invoke rcu_seq_end(&sup->srcu_gp_seq), queue a timer.
4- the sdp->work begin run, and call srcu_segcblist_advance() with current sup->srcu_gp_seq.
   so the step4's srcu callback and step5's barrier calback both to be run.
   all callback finished, but the len not yet update.
5- the srcu_barrier() return, begin call srcu_clean_up(), and then the srcu_clean_up
   find the a timer is still pending and the cb_len also not be zero, trigger WARN_ON()

right?

Thanks
Zqiang
   





> >  
> >  because you say the 5.srcu_barrier() can not intercept setp4 call_srcu() enqueue callback func, 
> >  that means that the step4 srcu callback shuold not be run, the cblist also not empty.
> >  
> >  Thanks
> >  Zqiang 
> >  
> >  
> >  Thanks
> >  Sunho Park
> >
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-27 12:40                 ` Zqiang
@ 2026-08-27 13:03                   ` Sunho Park
  2026-08-27 13:40                     ` Zqiang
  2026-08-27 13:41                     ` Zqiang
  0 siblings, 2 replies; 14+ messages in thread
From: Sunho Park @ 2026-08-27 13:03 UTC (permalink / raw)
  To: Zqiang, rcu; +Cc: paulmck, linux-kernel, syzbot+d4faf7db59e11f6fd1ab

On 8/27/26 21:40, Zqiang wrote:
> Now, I am trying to rephrase your problem in the following way:
> 
> 1- the step1 call_srcu() trigger SRCU grace period has been end and queue sdp->work.
> 2- the step4 queue srcu callback has been intercepted by step5 srcu_barrier()
>     and start a new SRCU grace period.
> 3- this new SRCU grace period end, and invoke rcu_seq_end(&sup->srcu_gp_seq), queue a timer.
> 4- the sdp->work begin run, and call srcu_segcblist_advance() with current sup->srcu_gp_seq.
>     so the step4's srcu callback and step5's barrier calback both to be run.
>     all callback finished, but the len not yet update.
> 5- the srcu_barrier() return, begin call srcu_clean_up(), and then the srcu_clean_up
>     find the a timer is still pending and the cb_len also not be zero, trigger WARN_ON()
> 
> right?
Exactly. The real callbacks are properly invoked as srcu_barrier() 
guarantees. The WARN is triggered only by the two transient states: a 
pending delay_work timer and a stale cblist len. So I think this is a 
false positive warning.

Sorry if my explanations were too wordy.

Thanks
Sunho Park

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-27 13:03                   ` Sunho Park
@ 2026-08-27 13:40                     ` Zqiang
  2026-08-27 13:41                     ` Zqiang
  1 sibling, 0 replies; 14+ messages in thread
From: Zqiang @ 2026-08-27 13:40 UTC (permalink / raw)
  To: Sunho Park, paulmck; +Cc: linux-kernel, syzbot+d4faf7db59e11f6fd1ab, rcu

> 
> On 8/27/26 21:40, Zqiang wrote:
> 
> > 
> > Now, I am trying to rephrase your problem in the following way:
> >  1- the step1 call_srcu() trigger SRCU grace period has been end and queue sdp->work.
> >  2- the step4 queue srcu callback has been intercepted by step5 srcu_barrier()
> >  and start a new SRCU grace period.
> >  3- this new SRCU grace period end, and invoke rcu_seq_end(&sup->srcu_gp_seq), queue a timer.
> >  4- the sdp->work begin run, and call srcu_segcblist_advance() with current sup->srcu_gp_seq.
> >  so the step4's srcu callback and step5's barrier calback both to be run.
> >  all callback finished, but the len not yet update.
> >  5- the srcu_barrier() return, begin call srcu_clean_up(), and then the srcu_clean_up
> >  find the a timer is still pending and the cb_len also not be zero, trigger WARN_ON()
> >  right?
> > 
> Exactly. The real callbacks are properly invoked as srcu_barrier() guarantees. The WARN is triggered only by the two transient states: a pending delay_work timer and a stale cblist len. So I think this is a false positive warning.
> 
> Sorry if my explanations were too wordy.

Would you like to send a patch to fix it ?

remove WARN_ON() or use rcu_segcblist_empty(&sdp->srcu_cblist) to replace rcu_segcblist_n_cbs(&sdp->srcu_cblist) ?

Let's hear Paul's opinion.

Thanks
Zqiang

> 
> Thanks
> Sunho Park
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20
  2026-08-27 13:03                   ` Sunho Park
  2026-08-27 13:40                     ` Zqiang
@ 2026-08-27 13:41                     ` Zqiang
  1 sibling, 0 replies; 14+ messages in thread
From: Zqiang @ 2026-08-27 13:41 UTC (permalink / raw)
  To: Sunho Park, paulmck; +Cc: linux-kernel, syzbot+d4faf7db59e11f6fd1ab, rcu

> 
> On 8/27/26 21:40, Zqiang wrote:
> 
> > 
> > Now, I am trying to rephrase your problem in the following way:
> >  1- the step1 call_srcu() trigger SRCU grace period has been end and queue sdp->work.
> >  2- the step4 queue srcu callback has been intercepted by step5 srcu_barrier()
> >  and start a new SRCU grace period.
> >  3- this new SRCU grace period end, and invoke rcu_seq_end(&sup->srcu_gp_seq), queue a timer.
> >  4- the sdp->work begin run, and call srcu_segcblist_advance() with current sup->srcu_gp_seq.
> >  so the step4's srcu callback and step5's barrier calback both to be run.
> >  all callback finished, but the len not yet update.
> >  5- the srcu_barrier() return, begin call srcu_clean_up(), and then the srcu_clean_up
> >  find the a timer is still pending and the cb_len also not be zero, trigger WARN_ON()
> >  right?
> > 
> Exactly. The real callbacks are properly invoked as srcu_barrier() guarantees. The WARN is triggered only by the two transient states: a pending delay_work timer and a stale cblist len. So I think this is a false positive warning.
> 
> Sorry if my explanations were too wordy.

Would you like to send a patch to fix it ?

remove WARN_ON() or use rcu_segcblist_empty(&sdp->srcu_cblist) to replace rcu_segcblist_n_cbs(&sdp->srcu_cblist) ?

Let's hear Paul's opinion.

Thanks
Zqiang

> 
> Thanks
> Sunho Park
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-08-27 13:41 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-24 10:56 [BUG] srcu: false-positive WARN in cleanup_srcu_struct() after 78a38cbf6f20 Sunho Park
2026-08-25 12:33 ` Zqiang
2026-08-25 16:50   ` Sunho Park
2026-08-26 13:13     ` Zqiang
2026-08-26 16:03       ` Sunho Park
2026-08-26 23:53         ` Zqiang
2026-08-27  9:11           ` Sunho Park
2026-08-27 11:13             ` Zqiang
2026-08-27 11:35               ` Zqiang
2026-08-27 12:30                 ` Sunho Park
2026-08-27 12:40                 ` Zqiang
2026-08-27 13:03                   ` Sunho Park
2026-08-27 13:40                     ` Zqiang
2026-08-27 13:41                     ` Zqiang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®