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