* [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®