* [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-10-27 16:38 [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race Andrea Parri
@ 2026-10-27 16:38 ` Andrea Parri
2026-10-28 17:26 ` Paul E. McKenney
2026-10-27 16:38 ` [PATCH -rcu/dev 2/4] hazptr: Check per-CPU slot owner with CONFIG_HAZPTR_DEBUG Andrea Parri
` (3 subsequent siblings)
4 siblings, 1 reply; 18+ messages in thread
From: Andrea Parri @ 2026-10-27 16:38 UTC (permalink / raw)
To: Paul E. McKenney, Mathieu Desnoyers
Cc: Andrea Parri, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
hazptrtorture can hang at shutdown when deferral is enabled, as in the
PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on it
never returns, until kvm.sh kills QEMU:
[ 1800.899278] hazptr-torture: Stopping hazptr_torture_do_pending task
[ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer task
[ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436 HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff: 48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq: 5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139 relundefer 714114
[ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
Summary: Potential hang (QEMU killed)
!!! PID 1393523 hung at 1980 vs. 1800 seconds
hazptr_torture_cleanup() stops the do_pending kthread first, then the
writer, then the readers. Once torture_must_stop(), do_pending makes
one last pass and leaves its loop, but unless stutter_will_wait(), that
pass releases the deferred hazard pointers of a single CPU.
Readers can also still defer after that pass, from the iteration they
are in when torture_must_stop() becomes true. Readers that have left
their loop hold no hazard pointer, so the writer waits on one of the 25
deferred hazard pointers above (reldefer - relundefer) that nothing
releases.
Stop the readers before do_pending, and do_pending before the writer.
do_pending still leaves its loop at torture_must_stop(), and now
releases the deferred hazard pointers of all CPUs once
torture_kthread_stopping() returns, that is, once
hazptr_torture_cleanup() stops it, after the readers. The writer can
still wait in ->sync(), but only until then.
This should be safe because readers defer only from their own loop,
which they have left once torture_stop_kthread() returns for them, and
the hazptr_torture_acquire() IPI runs synchronously within that loop.
The readers do not need the writer to stop: they leave their loop at
torture_must_stop(), after at most one stutter_wait().
do_pending keeps the torture_must_stop() check before its sleep. A
kthread_stop() that comes after that check and before the sleep starts
still delays do_pending's exit by a full kthread_do_pending_ms sleep,
however long that is. With the readers stopped first, this happens
only if do_pending passed the check before the test began stopping and
had not yet gone to sleep when kthread_stop() came, after all the
readers had stopped.
With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
with "End of test: SUCCESS" and with as many hazard pointers released
from the pending lists as were deferred (reldefer: 983727 relundefer
983727).
The hang was found with the help of an LLM.
Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred hazard pointers")
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
kernel/rcu/hazptrtorture.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
index 7c8b5899fb011..c46083a8af093 100644
--- a/kernel/rcu/hazptrtorture.c
+++ b/kernel/rcu/hazptrtorture.c
@@ -560,6 +560,9 @@ static int hazptr_torture_do_pending(void *arg)
// Omit stutter_wait() because this function needs to do cleanup.
} while (!torture_must_stop());
torture_kthread_stopping("hazptr_torture_do_pending");
+ /* The readers have stopped, so nothing new can be deferred. */
+ for_each_possible_cpu(cpu)
+ hazptr_torture_do_one_pending(cpu, &rand);
return 0;
}
@@ -773,9 +776,7 @@ hazptr_torture_cleanup(void)
return;
}
- torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
torture_stop_kthread(hazptr_torture_preempt, preempt_task);
- torture_stop_kthread(hazptr_torture_writer, writer_task);
if (reader_tasks) {
for (i = 0; i < nrealreaders; i++)
@@ -785,6 +786,14 @@ hazptr_torture_cleanup(void)
reader_tasks = NULL;
}
+ /*
+ * Stop the writer only after do_pending, and do_pending only after
+ * the readers: the writer may be waiting on hazard pointers that
+ * readers deferred, which do_pending releases once they stopped.
+ */
+ torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
+ torture_stop_kthread(hazptr_torture_writer, writer_task);
+
torture_stop_kthread(hazptr_torture_stats, stats_task);
/* Do torture-type-specific cleanup operations. */
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-10-27 16:38 ` [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping Andrea Parri
@ 2026-10-28 17:26 ` Paul E. McKenney
2026-10-28 23:51 ` Paul E. McKenney
0 siblings, 1 reply; 18+ messages in thread
From: Paul E. McKenney @ 2026-10-28 17:26 UTC (permalink / raw)
To: Andrea Parri
Cc: Mathieu Desnoyers, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
On Tue, Oct 27, 2026 at 05:38:03PM +0100, Andrea Parri wrote:
> hazptrtorture can hang at shutdown when deferral is enabled, as in the
> PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on it
> never returns, until kvm.sh kills QEMU:
>
> [ 1800.899278] hazptr-torture: Stopping hazptr_torture_do_pending task
> [ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer task
> [ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436 HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff: 48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq: 5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139 relundefer 714114
> [ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
>
> Summary: Potential hang (QEMU killed)
> !!! PID 1393523 hung at 1980 vs. 1800 seconds
>
> hazptr_torture_cleanup() stops the do_pending kthread first, then the
> writer, then the readers. Once torture_must_stop(), do_pending makes
> one last pass and leaves its loop, but unless stutter_will_wait(), that
> pass releases the deferred hazard pointers of a single CPU.
>
> Readers can also still defer after that pass, from the iteration they
> are in when torture_must_stop() becomes true. Readers that have left
> their loop hold no hazard pointer, so the writer waits on one of the 25
> deferred hazard pointers above (reldefer - relundefer) that nothing
> releases.
>
> Stop the readers before do_pending, and do_pending before the writer.
> do_pending still leaves its loop at torture_must_stop(), and now
> releases the deferred hazard pointers of all CPUs once
> torture_kthread_stopping() returns, that is, once
> hazptr_torture_cleanup() stops it, after the readers. The writer can
> still wait in ->sync(), but only until then.
>
> This should be safe because readers defer only from their own loop,
> which they have left once torture_stop_kthread() returns for them, and
> the hazptr_torture_acquire() IPI runs synchronously within that loop.
> The readers do not need the writer to stop: they leave their loop at
> torture_must_stop(), after at most one stutter_wait().
>
> do_pending keeps the torture_must_stop() check before its sleep. A
> kthread_stop() that comes after that check and before the sleep starts
> still delays do_pending's exit by a full kthread_do_pending_ms sleep,
> however long that is. With the readers stopped first, this happens
> only if do_pending passed the check before the test began stopping and
> had not yet gone to sleep when kthread_stop() came, after all the
> readers had stopped.
>
> With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
> with "End of test: SUCCESS" and with as many hazard pointers released
> from the pending lists as were deferred (reldefer: 983727 relundefer
> 983727).
>
> The hang was found with the help of an LLM.
>
> Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred hazard pointers")
> Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
Again, thank you!
Please see the question below.
Thanx, Paul
> ---
> kernel/rcu/hazptrtorture.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
> index 7c8b5899fb011..c46083a8af093 100644
> --- a/kernel/rcu/hazptrtorture.c
> +++ b/kernel/rcu/hazptrtorture.c
> @@ -560,6 +560,9 @@ static int hazptr_torture_do_pending(void *arg)
> // Omit stutter_wait() because this function needs to do cleanup.
> } while (!torture_must_stop());
The torture_must_stop() function expands to this condition:
torture_must_stop_irq() || kthread_should_stop()
The torture_must_stop_irq() function expands to this condition:
READ_ONCE(fullstop) != FULLSTOP_DONTSTOP
And the fullstop variable is set to FULLSTOP_RMMOD by
torture_cleanup_begin() (unless it has already been set to
FULLSTOP_SHUTDOWN).
And torture_cleanup_begin() is invoked by hazptr_torture_cleanup()
right at the beginning, before reaching any of the calls to
torture_stop_kthread().
So doesn't the above torture_must_stop() instead need to be
kthread_should_stop()?
As it is, hazptr_torture_do_pending() will still stop before the
hazptr_torture_reader() instances do, correct?
Or am I missing something subtle here?
> torture_kthread_stopping("hazptr_torture_do_pending");
> + /* The readers have stopped, so nothing new can be deferred. */
> + for_each_possible_cpu(cpu)
> + hazptr_torture_do_one_pending(cpu, &rand);
> return 0;
> }
>
> @@ -773,9 +776,7 @@ hazptr_torture_cleanup(void)
> return;
> }
>
> - torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
> torture_stop_kthread(hazptr_torture_preempt, preempt_task);
> - torture_stop_kthread(hazptr_torture_writer, writer_task);
>
> if (reader_tasks) {
> for (i = 0; i < nrealreaders; i++)
> @@ -785,6 +786,14 @@ hazptr_torture_cleanup(void)
> reader_tasks = NULL;
> }
>
> + /*
> + * Stop the writer only after do_pending, and do_pending only after
> + * the readers: the writer may be waiting on hazard pointers that
> + * readers deferred, which do_pending releases once they stopped.
> + */
> + torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
> + torture_stop_kthread(hazptr_torture_writer, writer_task);
> +
> torture_stop_kthread(hazptr_torture_stats, stats_task);
>
> /* Do torture-type-specific cleanup operations. */
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-10-28 17:26 ` Paul E. McKenney
@ 2026-10-28 23:51 ` Paul E. McKenney
2026-10-29 14:47 ` Andrea Parri
2026-11-03 4:50 ` Paul E. McKenney
0 siblings, 2 replies; 18+ messages in thread
From: Paul E. McKenney @ 2026-10-28 23:51 UTC (permalink / raw)
To: Andrea Parri
Cc: Mathieu Desnoyers, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
On Wed, Oct 28, 2026 at 10:26:18AM -0700, Paul E. McKenney wrote:
> On Tue, Oct 27, 2026 at 05:38:03PM +0100, Andrea Parri wrote:
> > hazptrtorture can hang at shutdown when deferral is enabled, as in the
> > PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on it
> > never returns, until kvm.sh kills QEMU:
> >
> > [ 1800.899278] hazptr-torture: Stopping hazptr_torture_do_pending task
> > [ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer task
> > [ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436 HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff: 48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq: 5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139 relundefer 714114
> > [ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
> >
> > Summary: Potential hang (QEMU killed)
> > !!! PID 1393523 hung at 1980 vs. 1800 seconds
> >
> > hazptr_torture_cleanup() stops the do_pending kthread first, then the
> > writer, then the readers. Once torture_must_stop(), do_pending makes
> > one last pass and leaves its loop, but unless stutter_will_wait(), that
> > pass releases the deferred hazard pointers of a single CPU.
> >
> > Readers can also still defer after that pass, from the iteration they
> > are in when torture_must_stop() becomes true. Readers that have left
> > their loop hold no hazard pointer, so the writer waits on one of the 25
> > deferred hazard pointers above (reldefer - relundefer) that nothing
> > releases.
> >
> > Stop the readers before do_pending, and do_pending before the writer.
> > do_pending still leaves its loop at torture_must_stop(), and now
> > releases the deferred hazard pointers of all CPUs once
> > torture_kthread_stopping() returns, that is, once
> > hazptr_torture_cleanup() stops it, after the readers. The writer can
> > still wait in ->sync(), but only until then.
> >
> > This should be safe because readers defer only from their own loop,
> > which they have left once torture_stop_kthread() returns for them, and
> > the hazptr_torture_acquire() IPI runs synchronously within that loop.
> > The readers do not need the writer to stop: they leave their loop at
> > torture_must_stop(), after at most one stutter_wait().
> >
> > do_pending keeps the torture_must_stop() check before its sleep. A
> > kthread_stop() that comes after that check and before the sleep starts
> > still delays do_pending's exit by a full kthread_do_pending_ms sleep,
> > however long that is. With the readers stopped first, this happens
> > only if do_pending passed the check before the test began stopping and
> > had not yet gone to sleep when kthread_stop() came, after all the
> > readers had stopped.
> >
> > With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
> > with "End of test: SUCCESS" and with as many hazard pointers released
> > from the pending lists as were deferred (reldefer: 983727 relundefer
> > 983727).
> >
> > The hang was found with the help of an LLM.
> >
> > Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred hazard pointers")
> > Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
>
> Again, thank you!
>
> Please see the question below.
>
> Thanx, Paul
>
> > ---
> > kernel/rcu/hazptrtorture.c | 13 +++++++++++--
> > 1 file changed, 11 insertions(+), 2 deletions(-)
> >
> > diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
> > index 7c8b5899fb011..c46083a8af093 100644
> > --- a/kernel/rcu/hazptrtorture.c
> > +++ b/kernel/rcu/hazptrtorture.c
> > @@ -560,6 +560,9 @@ static int hazptr_torture_do_pending(void *arg)
> > // Omit stutter_wait() because this function needs to do cleanup.
> > } while (!torture_must_stop());
>
> The torture_must_stop() function expands to this condition:
>
> torture_must_stop_irq() || kthread_should_stop()
>
> The torture_must_stop_irq() function expands to this condition:
>
> READ_ONCE(fullstop) != FULLSTOP_DONTSTOP
>
> And the fullstop variable is set to FULLSTOP_RMMOD by
> torture_cleanup_begin() (unless it has already been set to
> FULLSTOP_SHUTDOWN).
>
> And torture_cleanup_begin() is invoked by hazptr_torture_cleanup()
> right at the beginning, before reaching any of the calls to
> torture_stop_kthread().
>
> So doesn't the above torture_must_stop() instead need to be
> kthread_should_stop()?
>
> As it is, hazptr_torture_do_pending() will still stop before the
> hazptr_torture_reader() instances do, correct?
>
> Or am I missing something subtle here?
Well, one thing that I am missing is that changing from
torture_must_stop() to kthread_should_stop() might cause trouble
if rcutorture execution is terminated by system shutdown.
So my next suggestion is that this be added just after
hazptr_torture_reader() exits its do-while loop:
for_each_possible_cpu(cpu)
hazptr_torture_do_one_pending(cpu, &rand);
Thoughts?
Thanx, Paul
> > torture_kthread_stopping("hazptr_torture_do_pending");
> > + /* The readers have stopped, so nothing new can be deferred. */
> > + for_each_possible_cpu(cpu)
> > + hazptr_torture_do_one_pending(cpu, &rand);
> > return 0;
> > }
> >
> > @@ -773,9 +776,7 @@ hazptr_torture_cleanup(void)
> > return;
> > }
> >
> > - torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
> > torture_stop_kthread(hazptr_torture_preempt, preempt_task);
> > - torture_stop_kthread(hazptr_torture_writer, writer_task);
> >
> > if (reader_tasks) {
> > for (i = 0; i < nrealreaders; i++)
> > @@ -785,6 +786,14 @@ hazptr_torture_cleanup(void)
> > reader_tasks = NULL;
> > }
> >
> > + /*
> > + * Stop the writer only after do_pending, and do_pending only after
> > + * the readers: the writer may be waiting on hazard pointers that
> > + * readers deferred, which do_pending releases once they stopped.
> > + */
> > + torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
> > + torture_stop_kthread(hazptr_torture_writer, writer_task);
> > +
> > torture_stop_kthread(hazptr_torture_stats, stats_task);
> >
> > /* Do torture-type-specific cleanup operations. */
> > --
> > 2.53.0
> >
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-10-28 23:51 ` Paul E. McKenney
@ 2026-10-29 14:47 ` Andrea Parri
2026-11-03 4:50 ` Paul E. McKenney
1 sibling, 0 replies; 18+ messages in thread
From: Andrea Parri @ 2026-10-29 14:47 UTC (permalink / raw)
To: Paul E. McKenney
Cc: Andrea Parri, Mathieu Desnoyers, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Lai Jiangshan, Zqiang,
Gary Guo, Bradley Morgan, Kunwu Chan, rcu, lkmm, linux-kernel
On Wed, Oct 28, 2026 at 04:51:12PM -0700, Paul E. McKenney wrote:
> On Wed, Oct 28, 2026 at 10:26:18AM -0700, Paul E. McKenney wrote:
[...]
> > So doesn't the above torture_must_stop() instead need to be
> > kthread_should_stop()?
> >
> > As it is, hazptr_torture_do_pending() will still stop before the
> > hazptr_torture_reader() instances do, correct?
Its loop does exit first, but v1's for_each_possible_cpu() pass comes
after torture_kthread_stopping(), which returns only once
hazptr_torture_cleanup() stops do_pending, after the readers.
> Well, one thing that I am missing is that changing from
> torture_must_stop() to kthread_should_stop() might cause trouble
> if rcutorture execution is terminated by system shutdown.
Agreed: on a shutdown that hazptrtorture did not start itself, the
reboot notifier sets FULLSTOP_SHUTDOWN, and nothing calls
kthread_stop().
> So my next suggestion is that this be added just after
> hazptr_torture_reader() exits its do-while loop:
>
> for_each_possible_cpu(cpu)
> hazptr_torture_do_one_pending(cpu, &rand);
>
> Thoughts?
Works for me, thanks! I have been testing it, instead of the
for_each_possible_cpu() pass that v1 adds after
torture_kthread_stopping(), and keeping the torture_must_stop() loop
condition. I will send v2 with it if it holds up.
Thanks,
Andrea
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-10-28 23:51 ` Paul E. McKenney
2026-10-29 14:47 ` Andrea Parri
@ 2026-11-03 4:50 ` Paul E. McKenney
2026-11-03 11:36 ` Bradley Morgan
2026-11-03 19:05 ` Andrea Parri
1 sibling, 2 replies; 18+ messages in thread
From: Paul E. McKenney @ 2026-11-03 4:50 UTC (permalink / raw)
To: Andrea Parri
Cc: Mathieu Desnoyers, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
On Wed, Oct 28, 2026 at 04:51:12PM -0700, Paul E. McKenney wrote:
> On Wed, Oct 28, 2026 at 10:26:18AM -0700, Paul E. McKenney wrote:
> > On Tue, Oct 27, 2026 at 05:38:03PM +0100, Andrea Parri wrote:
> > > hazptrtorture can hang at shutdown when deferral is enabled, as in the
> > > PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on it
> > > never returns, until kvm.sh kills QEMU:
> > >
> > > [ 1800.899278] hazptr-torture: Stopping hazptr_torture_do_pending task
> > > [ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer task
> > > [ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436 HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff: 48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq: 5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139 relundefer 714114
> > > [ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
> > >
> > > Summary: Potential hang (QEMU killed)
> > > !!! PID 1393523 hung at 1980 vs. 1800 seconds
> > >
> > > hazptr_torture_cleanup() stops the do_pending kthread first, then the
> > > writer, then the readers. Once torture_must_stop(), do_pending makes
> > > one last pass and leaves its loop, but unless stutter_will_wait(), that
> > > pass releases the deferred hazard pointers of a single CPU.
> > >
> > > Readers can also still defer after that pass, from the iteration they
> > > are in when torture_must_stop() becomes true. Readers that have left
> > > their loop hold no hazard pointer, so the writer waits on one of the 25
> > > deferred hazard pointers above (reldefer - relundefer) that nothing
> > > releases.
> > >
> > > Stop the readers before do_pending, and do_pending before the writer.
> > > do_pending still leaves its loop at torture_must_stop(), and now
> > > releases the deferred hazard pointers of all CPUs once
> > > torture_kthread_stopping() returns, that is, once
> > > hazptr_torture_cleanup() stops it, after the readers. The writer can
> > > still wait in ->sync(), but only until then.
> > >
> > > This should be safe because readers defer only from their own loop,
> > > which they have left once torture_stop_kthread() returns for them, and
> > > the hazptr_torture_acquire() IPI runs synchronously within that loop.
> > > The readers do not need the writer to stop: they leave their loop at
> > > torture_must_stop(), after at most one stutter_wait().
> > >
> > > do_pending keeps the torture_must_stop() check before its sleep. A
> > > kthread_stop() that comes after that check and before the sleep starts
> > > still delays do_pending's exit by a full kthread_do_pending_ms sleep,
> > > however long that is. With the readers stopped first, this happens
> > > only if do_pending passed the check before the test began stopping and
> > > had not yet gone to sleep when kthread_stop() came, after all the
> > > readers had stopped.
> > >
> > > With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
> > > with "End of test: SUCCESS" and with as many hazard pointers released
> > > from the pending lists as were deferred (reldefer: 983727 relundefer
> > > 983727).
> > >
> > > The hang was found with the help of an LLM.
> > >
> > > Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred hazard pointers")
> > > Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
> >
> > Again, thank you!
> >
> > Please see the question below.
> >
> > Thanx, Paul
> >
> > > ---
> > > kernel/rcu/hazptrtorture.c | 13 +++++++++++--
> > > 1 file changed, 11 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
> > > index 7c8b5899fb011..c46083a8af093 100644
> > > --- a/kernel/rcu/hazptrtorture.c
> > > +++ b/kernel/rcu/hazptrtorture.c
> > > @@ -560,6 +560,9 @@ static int hazptr_torture_do_pending(void *arg)
> > > // Omit stutter_wait() because this function needs to do cleanup.
> > > } while (!torture_must_stop());
> >
> > The torture_must_stop() function expands to this condition:
> >
> > torture_must_stop_irq() || kthread_should_stop()
> >
> > The torture_must_stop_irq() function expands to this condition:
> >
> > READ_ONCE(fullstop) != FULLSTOP_DONTSTOP
> >
> > And the fullstop variable is set to FULLSTOP_RMMOD by
> > torture_cleanup_begin() (unless it has already been set to
> > FULLSTOP_SHUTDOWN).
> >
> > And torture_cleanup_begin() is invoked by hazptr_torture_cleanup()
> > right at the beginning, before reaching any of the calls to
> > torture_stop_kthread().
> >
> > So doesn't the above torture_must_stop() instead need to be
> > kthread_should_stop()?
> >
> > As it is, hazptr_torture_do_pending() will still stop before the
> > hazptr_torture_reader() instances do, correct?
> >
> > Or am I missing something subtle here?
>
> Well, one thing that I am missing is that changing from
> torture_must_stop() to kthread_should_stop() might cause trouble
> if rcutorture execution is terminated by system shutdown.
>
> So my next suggestion is that this be added just after
> hazptr_torture_reader() exits its do-while loop:
>
> for_each_possible_cpu(cpu)
> hazptr_torture_do_one_pending(cpu, &rand);
>
> Thoughts?
How about like this?
Thanx, Paul
------------------------------------------------------------------------
commit 863f3c68c5dce601456368a9152de0239421cef9
Author: Andrea Parri <parri.andrea@gmail.com>
Date: Tue Oct 27 17:38:03 2026 +0100
hazptrtorture: Release all deferred hazard pointers before stopping
hazptrtorture can hang at shutdown when deferral is enabled, as in the
PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on it
never returns, until kvm.sh kills QEMU:
[ 1800.899278] hazptr-torture: Stopping hazptr_torture_do_pending task
[ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer task
[ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436 HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff: 48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq: 5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139 relundefer 714114
[ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
Summary: Potential hang (QEMU killed)
!!! PID 1393523 hung at 1980 vs. 1800 seconds
hazptr_torture_cleanup() stops the kthreads in random order, which
means that there could be a reader thread still running after
the do_pending kthread does its last cleanup pass. If the writer
is still running at that point, any remaining deferred hazard
pointers will never be cleaned up and the writer can wait forever
if it is unlucky enough to be waiting on one of the deferred
hazard poiners.
Therefore, make each reader clean up all deferred hazard pointers,
which means that after the last reader finishes, there can no
longer be any deferred hazard pointers, which means that the
writer can terminate promptly.
With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
with "End of test: SUCCESS" and with as many hazard pointers released
from the pending lists as were deferred (reldefer: 983727 relundefer
983727).
The hang was found with the help of an LLM.
[ paulmck: Make readers release deferred hazard pointers. ]
Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred hazard pointers")
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
index 7c8b5899fb011..3059ccb01979d 100644
--- a/kernel/rcu/hazptrtorture.c
+++ b/kernel/rcu/hazptrtorture.c
@@ -449,6 +449,28 @@ static void hazptr_torture_defer(struct hazptr_pending *hppp, struct torture_ran
atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_defer, raw_smp_processor_id()));
}
+/*
+ * Release the specified CPU's set of deferred/pending hazard pointers.
+ */
+static void hazptr_torture_do_one_pending(int cpu, struct torture_random_state *trsp)
+{
+ struct hazptr_pending *hppp;
+ struct hazptr_pending *hppp1;
+ struct llist_head *llhp;
+ struct llist_node *llnp;
+
+ llhp = per_cpu_ptr(&hazptr_pending, cpu);
+ llnp = llist_del_all(llhp);
+ if (!llnp)
+ return;
+ llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
+ hazptr_torture_reader_tail(hppp, trsp);
+ atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
+ raw_smp_processor_id()));
+ kfree(hppp);
+ }
+}
+
/*
* Hazard-pointer torture reader kthread. Repeatedly dereferences
* hazptr_torture_current, incrementing the corresponding element of the
@@ -513,31 +535,13 @@ static int hazptr_torture_reader(void *arg)
stutter_wait("hazptr_torture_reader");
} while (!torture_must_stop());
torture_kthread_stopping("hazptr_torture_reader");
+ // Each reader clears all CPUs, so nothing is left after all
+ // readers exit their for_each_possible_cpu() loops.
+ for_each_possible_cpu(cpu)
+ hazptr_torture_do_one_pending(cpu, &rand);
return 0;
}
-/*
- * Release the specified CPU's set of deferred/pending hazard pointers.
- */
-static void hazptr_torture_do_one_pending(int cpu, struct torture_random_state *trsp)
-{
- struct hazptr_pending *hppp;
- struct hazptr_pending *hppp1;
- struct llist_head *llhp;
- struct llist_node *llnp;
-
- llhp = per_cpu_ptr(&hazptr_pending, cpu);
- llnp = llist_del_all(llhp);
- if (!llnp)
- return;
- llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
- hazptr_torture_reader_tail(hppp, trsp);
- atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
- raw_smp_processor_id()));
- kfree(hppp);
- }
-}
-
/*
* Hazard-pointer release of deferred/pending hazard pointers.
*/
@@ -560,6 +564,7 @@ static int hazptr_torture_do_pending(void *arg)
// Omit stutter_wait() because this function needs to do cleanup.
} while (!torture_must_stop());
torture_kthread_stopping("hazptr_torture_do_pending");
+ // Readers clean up after themselves before exit.
return 0;
}
@@ -773,9 +778,7 @@ hazptr_torture_cleanup(void)
return;
}
- torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
torture_stop_kthread(hazptr_torture_preempt, preempt_task);
- torture_stop_kthread(hazptr_torture_writer, writer_task);
if (reader_tasks) {
for (i = 0; i < nrealreaders; i++)
@@ -785,6 +788,14 @@ hazptr_torture_cleanup(void)
reader_tasks = NULL;
}
+ /*
+ * Stop the writer only after do_pending, and do_pending only after
+ * the readers: the writer may be waiting on hazard pointers that
+ * readers deferred, which do_pending releases once they stopped.
+ */
+ torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
+ torture_stop_kthread(hazptr_torture_writer, writer_task);
+
torture_stop_kthread(hazptr_torture_stats, stats_task);
/* Do torture-type-specific cleanup operations. */
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-11-03 4:50 ` Paul E. McKenney
@ 2026-11-03 11:36 ` Bradley Morgan
2026-11-03 17:28 ` Paul E. McKenney
2026-11-03 19:05 ` Andrea Parri
1 sibling, 1 reply; 18+ messages in thread
From: Bradley Morgan @ 2026-11-03 11:36 UTC (permalink / raw)
To: paulmck, Paul E. McKenney, Andrea Parri
Cc: Mathieu Desnoyers, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Kunwu Chan, rcu,
lkmm, linux-kernel
On 3 November 2026 04:50:51 GMT, "Paul E. McKenney" <paulmck@kernel.org>
wrote:
>On Wed, Oct 28, 2026 at 04:51:12PM -0700, Paul E. McKenney wrote:
>> On Wed, Oct 28, 2026 at 10:26:18AM -0700, Paul E. McKenney wrote:
>> > On Tue, Oct 27, 2026 at 05:38:03PM +0100, Andrea Parri wrote:
>> > > hazptrtorture can hang at shutdown when deferral is enabled, as in
>the
>> > > PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on
>it
>> > > never returns, until kvm.sh kills QEMU:
>> > >
>> > > [ 1800.899278] hazptr-torture: Stopping
>hazptr_torture_do_pending task
>> > > [ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer
>task
>> > > [ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436
>HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff:
>48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq:
>5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139
>relundefer 714114
>> > > [ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
>> > >
>> > > Summary: Potential hang (QEMU killed)
>> > > !!! PID 1393523 hung at 1980 vs. 1800 seconds
>> > >
>> > > hazptr_torture_cleanup() stops the do_pending kthread first, then
>the
>> > > writer, then the readers. Once torture_must_stop(), do_pending
>makes
>> > > one last pass and leaves its loop, but unless stutter_will_wait(),
>that
>> > > pass releases the deferred hazard pointers of a single CPU.
>> > >
>> > > Readers can also still defer after that pass, from the iteration
>they
>> > > are in when torture_must_stop() becomes true. Readers that have
>left
>> > > their loop hold no hazard pointer, so the writer waits on one of the
>25
>> > > deferred hazard pointers above (reldefer - relundefer) that nothing
>> > > releases.
>> > >
>> > > Stop the readers before do_pending, and do_pending before the
>writer.
>> > > do_pending still leaves its loop at torture_must_stop(), and now
>> > > releases the deferred hazard pointers of all CPUs once
>> > > torture_kthread_stopping() returns, that is, once
>> > > hazptr_torture_cleanup() stops it, after the readers. The writer
>can
>> > > still wait in ->sync(), but only until then.
>> > >
>> > > This should be safe because readers defer only from their own loop,
>> > > which they have left once torture_stop_kthread() returns for them,
>and
>> > > the hazptr_torture_acquire() IPI runs synchronously within that
>loop.
>> > > The readers do not need the writer to stop: they leave their loop at
>> > > torture_must_stop(), after at most one stutter_wait().
>> > >
>> > > do_pending keeps the torture_must_stop() check before its sleep. A
>> > > kthread_stop() that comes after that check and before the sleep
>starts
>> > > still delays do_pending's exit by a full kthread_do_pending_ms
>sleep,
>> > > however long that is. With the readers stopped first, this happens
>> > > only if do_pending passed the check before the test began stopping
>and
>> > > had not yet gone to sleep when kthread_stop() came, after all the
>> > > readers had stopped.
>> > >
>> > > With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
>> > > with "End of test: SUCCESS" and with as many hazard pointers
>released
>> > > from the pending lists as were deferred (reldefer: 983727 relundefer
>> > > 983727).
>> > >
>> > > The hang was found with the help of an LLM.
>> > >
>> > > Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred
>hazard pointers")
>> > > Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
>> >
>> > Again, thank you!
>> >
>> > Please see the question below.
>> >
>> > Thanx, Paul
>> >
>> > > ---
>> > > kernel/rcu/hazptrtorture.c | 13 +++++++++++--
>> > > 1 file changed, 11 insertions(+), 2 deletions(-)
>> > >
>> > > diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
>> > > index 7c8b5899fb011..c46083a8af093 100644
>> > > --- a/kernel/rcu/hazptrtorture.c
>> > > +++ b/kernel/rcu/hazptrtorture.c
>> > > @@ -560,6 +560,9 @@ static int hazptr_torture_do_pending(void *arg)
>> > > // Omit stutter_wait() because this function needs to do cleanup.
>> > > } while (!torture_must_stop());
>> >
>> > The torture_must_stop() function expands to this condition:
>> >
>> > torture_must_stop_irq() || kthread_should_stop()
>> >
>> > The torture_must_stop_irq() function expands to this condition:
>> >
>> > READ_ONCE(fullstop) != FULLSTOP_DONTSTOP
>> >
>> > And the fullstop variable is set to FULLSTOP_RMMOD by
>> > torture_cleanup_begin() (unless it has already been set to
>> > FULLSTOP_SHUTDOWN).
>> >
>> > And torture_cleanup_begin() is invoked by hazptr_torture_cleanup()
>> > right at the beginning, before reaching any of the calls to
>> > torture_stop_kthread().
>> >
>> > So doesn't the above torture_must_stop() instead need to be
>> > kthread_should_stop()?
>> >
>> > As it is, hazptr_torture_do_pending() will still stop before the
>> > hazptr_torture_reader() instances do, correct?
>> >
>> > Or am I missing something subtle here?
>>
>> Well, one thing that I am missing is that changing from
>> torture_must_stop() to kthread_should_stop() might cause trouble
>> if rcutorture execution is terminated by system shutdown.
>>
>> So my next suggestion is that this be added just after
>> hazptr_torture_reader() exits its do-while loop:
>>
>> for_each_possible_cpu(cpu)
>> hazptr_torture_do_one_pending(cpu, &rand);
>>
>> Thoughts?
>
>How about like this?
>
> Thanx, Paul
>
>------------------------------------------------------------------------
>
>commit 863f3c68c5dce601456368a9152de0239421cef9
>Author: Andrea Parri <parri.andrea@gmail.com>
>Date: Tue Oct 27 17:38:03 2026 +0100
>
> hazptrtorture: Release all deferred hazard pointers before stopping
>
> hazptrtorture can hang at shutdown when deferral is enabled, as in the
> PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on it
> never returns, until kvm.sh kills QEMU:
>
> [ 1800.899278] hazptr-torture: Stopping hazptr_torture_do_pending task
> [ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer task
> [ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436 HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff: 48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq: 5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139 relundefer 714114
> [ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
>
> Summary: Potential hang (QEMU killed)
> !!! PID 1393523 hung at 1980 vs. 1800 seconds
>
> hazptr_torture_cleanup() stops the kthreads in random order, which
> means that there could be a reader thread still running after
> the do_pending kthread does its last cleanup pass. If the writer
> is still running at that point, any remaining deferred hazard
> pointers will never be cleaned up and the writer can wait forever
> if it is unlucky enough to be waiting on one of the deferred
> hazard poiners.
>
> Therefore, make each reader clean up all deferred hazard pointers,
> which means that after the last reader finishes, there can no
> longer be any deferred hazard pointers, which means that the
> writer can terminate promptly.
>
> With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
> with "End of test: SUCCESS" and with as many hazard pointers released
> from the pending lists as were deferred (reldefer: 983727 relundefer
> 983727).
>
> The hang was found with the help of an LLM.
>
> [ paulmck: Make readers release deferred hazard pointers. ]
>
> Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred hazard pointers")
> Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
>
>diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
>index 7c8b5899fb011..3059ccb01979d 100644
>--- a/kernel/rcu/hazptrtorture.c
>+++ b/kernel/rcu/hazptrtorture.c
>@@ -449,6 +449,28 @@ static void hazptr_torture_defer(struct hazptr_pending *hppp, struct torture_ran
> atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_defer, raw_smp_processor_id()));
> }
>
>+/*
>+ * Release the specified CPU's set of deferred/pending hazard pointers.
>+ */
>+static void hazptr_torture_do_one_pending(int cpu, struct torture_random_state *trsp)
>+{
>+ struct hazptr_pending *hppp;
>+ struct hazptr_pending *hppp1;
>+ struct llist_head *llhp;
>+ struct llist_node *llnp;
>+
>+ llhp = per_cpu_ptr(&hazptr_pending, cpu);
>+ llnp = llist_del_all(llhp);
>+ if (!llnp)
>+ return;
>+ llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
>+ hazptr_torture_reader_tail(hppp, trsp);
>+ atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
>+ raw_smp_processor_id()));
>+ kfree(hppp);
>+ }
>+}
>+
> /*
> * Hazard-pointer torture reader kthread. Repeatedly dereferences
> * hazptr_torture_current, incrementing the corresponding element of the
>@@ -513,31 +535,13 @@ static int hazptr_torture_reader(void *arg)
> stutter_wait("hazptr_torture_reader");
> } while (!torture_must_stop());
> torture_kthread_stopping("hazptr_torture_reader");
>+ // Each reader clears all CPUs, so nothing is left after all
>+ // readers exit their for_each_possible_cpu() loops.
>+ for_each_possible_cpu(cpu)
>+ hazptr_torture_do_one_pending(cpu, &rand);
> return 0;
> }
>
>-/*
>- * Release the specified CPU's set of deferred/pending hazard pointers.
>- */
>-static void hazptr_torture_do_one_pending(int cpu, struct torture_random_state *trsp)
>-{
>- struct hazptr_pending *hppp;
>- struct hazptr_pending *hppp1;
>- struct llist_head *llhp;
>- struct llist_node *llnp;
>-
>- llhp = per_cpu_ptr(&hazptr_pending, cpu);
>- llnp = llist_del_all(llhp);
>- if (!llnp)
>- return;
>- llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
>- hazptr_torture_reader_tail(hppp, trsp);
>- atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
>- raw_smp_processor_id()));
>- kfree(hppp);
>- }
>-}
>-
> /*
> * Hazard-pointer release of deferred/pending hazard pointers.
> */
>@@ -560,6 +564,7 @@ static int hazptr_torture_do_pending(void *arg)
> // Omit stutter_wait() because this function needs to do cleanup.
> } while (!torture_must_stop());
> torture_kthread_stopping("hazptr_torture_do_pending");
>+ // Readers clean up after themselves before exit.
> return 0;
> }
>
>@@ -773,9 +778,7 @@ hazptr_torture_cleanup(void)
> return;
> }
>
>- torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
> torture_stop_kthread(hazptr_torture_preempt, preempt_task);
>- torture_stop_kthread(hazptr_torture_writer, writer_task);
>
> if (reader_tasks) {
> for (i = 0; i < nrealreaders; i++)
>@@ -785,6 +788,14 @@ hazptr_torture_cleanup(void)
> reader_tasks = NULL;
> }
>
>+ /*
>+ * Stop the writer only after do_pending, and do_pending only after
>+ * the readers: the writer may be waiting on hazard pointers that
>+ * readers deferred, which do_pending releases once they stopped.
>+ */
>+ torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
>+ torture_stop_kthread(hazptr_torture_writer, writer_task);
>+
> torture_stop_kthread(hazptr_torture_stats, stats_task);
>
> /* Do torture-type-specific cleanup operations. */
This looks okay to me.
--- Thanks!
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-11-03 11:36 ` Bradley Morgan
@ 2026-11-03 17:28 ` Paul E. McKenney
0 siblings, 0 replies; 18+ messages in thread
From: Paul E. McKenney @ 2026-11-03 17:28 UTC (permalink / raw)
To: Bradley Morgan
Cc: Andrea Parri, Mathieu Desnoyers, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Lai Jiangshan, Zqiang,
Gary Guo, Kunwu Chan, rcu, lkmm, linux-kernel
On Tue, Nov 03, 2026 at 11:36:42AM +0000, Bradley Morgan wrote:
> On 3 November 2026 04:50:51 GMT, "Paul E. McKenney" <paulmck@kernel.org>
> wrote:
> >On Wed, Oct 28, 2026 at 04:51:12PM -0700, Paul E. McKenney wrote:
> >> On Wed, Oct 28, 2026 at 10:26:18AM -0700, Paul E. McKenney wrote:
> >> > On Tue, Oct 27, 2026 at 05:38:03PM +0100, Andrea Parri wrote:
> >> > > hazptrtorture can hang at shutdown when deferral is enabled, as in
> >the
> >> > > PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on
> >it
> >> > > never returns, until kvm.sh kills QEMU:
> >> > >
> >> > > [ 1800.899278] hazptr-torture: Stopping
> >hazptr_torture_do_pending task
> >> > > [ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer
> >task
> >> > > [ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436
> >HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff:
> >48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq:
> >5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139
> >relundefer 714114
> >> > > [ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
> >> > >
> >> > > Summary: Potential hang (QEMU killed)
> >> > > !!! PID 1393523 hung at 1980 vs. 1800 seconds
> >> > >
> >> > > hazptr_torture_cleanup() stops the do_pending kthread first, then
> >the
> >> > > writer, then the readers. Once torture_must_stop(), do_pending
> >makes
> >> > > one last pass and leaves its loop, but unless stutter_will_wait(),
> >that
> >> > > pass releases the deferred hazard pointers of a single CPU.
> >> > >
> >> > > Readers can also still defer after that pass, from the iteration
> >they
> >> > > are in when torture_must_stop() becomes true. Readers that have
> >left
> >> > > their loop hold no hazard pointer, so the writer waits on one of the
> >25
> >> > > deferred hazard pointers above (reldefer - relundefer) that nothing
> >> > > releases.
> >> > >
> >> > > Stop the readers before do_pending, and do_pending before the
> >writer.
> >> > > do_pending still leaves its loop at torture_must_stop(), and now
> >> > > releases the deferred hazard pointers of all CPUs once
> >> > > torture_kthread_stopping() returns, that is, once
> >> > > hazptr_torture_cleanup() stops it, after the readers. The writer
> >can
> >> > > still wait in ->sync(), but only until then.
> >> > >
> >> > > This should be safe because readers defer only from their own loop,
> >> > > which they have left once torture_stop_kthread() returns for them,
> >and
> >> > > the hazptr_torture_acquire() IPI runs synchronously within that
> >loop.
> >> > > The readers do not need the writer to stop: they leave their loop at
> >> > > torture_must_stop(), after at most one stutter_wait().
> >> > >
> >> > > do_pending keeps the torture_must_stop() check before its sleep. A
> >> > > kthread_stop() that comes after that check and before the sleep
> >starts
> >> > > still delays do_pending's exit by a full kthread_do_pending_ms
> >sleep,
> >> > > however long that is. With the readers stopped first, this happens
> >> > > only if do_pending passed the check before the test began stopping
> >and
> >> > > had not yet gone to sleep when kthread_stop() came, after all the
> >> > > readers had stopped.
> >> > >
> >> > > With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
> >> > > with "End of test: SUCCESS" and with as many hazard pointers
> >released
> >> > > from the pending lists as were deferred (reldefer: 983727 relundefer
> >> > > 983727).
> >> > >
> >> > > The hang was found with the help of an LLM.
> >> > >
> >> > > Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred
> >hazard pointers")
> >> > > Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
> >> >
> >> > Again, thank you!
> >> >
> >> > Please see the question below.
> >> >
> >> > Thanx, Paul
> >> >
> >> > > ---
> >> > > kernel/rcu/hazptrtorture.c | 13 +++++++++++--
> >> > > 1 file changed, 11 insertions(+), 2 deletions(-)
> >> > >
> >> > > diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
> >> > > index 7c8b5899fb011..c46083a8af093 100644
> >> > > --- a/kernel/rcu/hazptrtorture.c
> >> > > +++ b/kernel/rcu/hazptrtorture.c
> >> > > @@ -560,6 +560,9 @@ static int hazptr_torture_do_pending(void *arg)
> >> > > // Omit stutter_wait() because this function needs to do cleanup.
> >> > > } while (!torture_must_stop());
> >> >
> >> > The torture_must_stop() function expands to this condition:
> >> >
> >> > torture_must_stop_irq() || kthread_should_stop()
> >> >
> >> > The torture_must_stop_irq() function expands to this condition:
> >> >
> >> > READ_ONCE(fullstop) != FULLSTOP_DONTSTOP
> >> >
> >> > And the fullstop variable is set to FULLSTOP_RMMOD by
> >> > torture_cleanup_begin() (unless it has already been set to
> >> > FULLSTOP_SHUTDOWN).
> >> >
> >> > And torture_cleanup_begin() is invoked by hazptr_torture_cleanup()
> >> > right at the beginning, before reaching any of the calls to
> >> > torture_stop_kthread().
> >> >
> >> > So doesn't the above torture_must_stop() instead need to be
> >> > kthread_should_stop()?
> >> >
> >> > As it is, hazptr_torture_do_pending() will still stop before the
> >> > hazptr_torture_reader() instances do, correct?
> >> >
> >> > Or am I missing something subtle here?
> >>
> >> Well, one thing that I am missing is that changing from
> >> torture_must_stop() to kthread_should_stop() might cause trouble
> >> if rcutorture execution is terminated by system shutdown.
> >>
> >> So my next suggestion is that this be added just after
> >> hazptr_torture_reader() exits its do-while loop:
> >>
> >> for_each_possible_cpu(cpu)
> >> hazptr_torture_do_one_pending(cpu, &rand);
> >>
> >> Thoughts?
> >
> >How about like this?
> >
> > Thanx, Paul
> >
> >------------------------------------------------------------------------
> >
> >commit 863f3c68c5dce601456368a9152de0239421cef9
> >Author: Andrea Parri <parri.andrea@gmail.com>
> >Date: Tue Oct 27 17:38:03 2026 +0100
> >
> > hazptrtorture: Release all deferred hazard pointers before stopping
> >
> > hazptrtorture can hang at shutdown when deferral is enabled, as in the
> > PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on it
> > never returns, until kvm.sh kills QEMU:
> >
> > [ 1800.899278] hazptr-torture: Stopping hazptr_torture_do_pending task
> > [ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer task
> > [ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436 HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff: 48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq: 5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139 relundefer 714114
> > [ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
> >
> > Summary: Potential hang (QEMU killed)
> > !!! PID 1393523 hung at 1980 vs. 1800 seconds
> >
> > hazptr_torture_cleanup() stops the kthreads in random order, which
> > means that there could be a reader thread still running after
> > the do_pending kthread does its last cleanup pass. If the writer
> > is still running at that point, any remaining deferred hazard
> > pointers will never be cleaned up and the writer can wait forever
> > if it is unlucky enough to be waiting on one of the deferred
> > hazard poiners.
> >
> > Therefore, make each reader clean up all deferred hazard pointers,
> > which means that after the last reader finishes, there can no
> > longer be any deferred hazard pointers, which means that the
> > writer can terminate promptly.
> >
> > With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
> > with "End of test: SUCCESS" and with as many hazard pointers released
> > from the pending lists as were deferred (reldefer: 983727 relundefer
> > 983727).
> >
> > The hang was found with the help of an LLM.
> >
> > [ paulmck: Make readers release deferred hazard pointers. ]
> >
> > Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred hazard pointers")
> > Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
> > Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
> >
> >diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
> >index 7c8b5899fb011..3059ccb01979d 100644
> >--- a/kernel/rcu/hazptrtorture.c
> >+++ b/kernel/rcu/hazptrtorture.c
> >@@ -449,6 +449,28 @@ static void hazptr_torture_defer(struct hazptr_pending *hppp, struct torture_ran
> > atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_defer, raw_smp_processor_id()));
> > }
> >
> >+/*
> >+ * Release the specified CPU's set of deferred/pending hazard pointers.
> >+ */
> >+static void hazptr_torture_do_one_pending(int cpu, struct torture_random_state *trsp)
> >+{
> >+ struct hazptr_pending *hppp;
> >+ struct hazptr_pending *hppp1;
> >+ struct llist_head *llhp;
> >+ struct llist_node *llnp;
> >+
> >+ llhp = per_cpu_ptr(&hazptr_pending, cpu);
> >+ llnp = llist_del_all(llhp);
> >+ if (!llnp)
> >+ return;
> >+ llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
> >+ hazptr_torture_reader_tail(hppp, trsp);
> >+ atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
> >+ raw_smp_processor_id()));
> >+ kfree(hppp);
> >+ }
> >+}
> >+
> > /*
> > * Hazard-pointer torture reader kthread. Repeatedly dereferences
> > * hazptr_torture_current, incrementing the corresponding element of the
> >@@ -513,31 +535,13 @@ static int hazptr_torture_reader(void *arg)
> > stutter_wait("hazptr_torture_reader");
> > } while (!torture_must_stop());
> > torture_kthread_stopping("hazptr_torture_reader");
> >+ // Each reader clears all CPUs, so nothing is left after all
> >+ // readers exit their for_each_possible_cpu() loops.
> >+ for_each_possible_cpu(cpu)
> >+ hazptr_torture_do_one_pending(cpu, &rand);
> > return 0;
> > }
> >
> >-/*
> >- * Release the specified CPU's set of deferred/pending hazard pointers.
> >- */
> >-static void hazptr_torture_do_one_pending(int cpu, struct torture_random_state *trsp)
> >-{
> >- struct hazptr_pending *hppp;
> >- struct hazptr_pending *hppp1;
> >- struct llist_head *llhp;
> >- struct llist_node *llnp;
> >-
> >- llhp = per_cpu_ptr(&hazptr_pending, cpu);
> >- llnp = llist_del_all(llhp);
> >- if (!llnp)
> >- return;
> >- llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
> >- hazptr_torture_reader_tail(hppp, trsp);
> >- atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
> >- raw_smp_processor_id()));
> >- kfree(hppp);
> >- }
> >-}
> >-
> > /*
> > * Hazard-pointer release of deferred/pending hazard pointers.
> > */
> >@@ -560,6 +564,7 @@ static int hazptr_torture_do_pending(void *arg)
> > // Omit stutter_wait() because this function needs to do cleanup.
> > } while (!torture_must_stop());
> > torture_kthread_stopping("hazptr_torture_do_pending");
> >+ // Readers clean up after themselves before exit.
> > return 0;
> > }
> >
> >@@ -773,9 +778,7 @@ hazptr_torture_cleanup(void)
> > return;
> > }
> >
> >- torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
> > torture_stop_kthread(hazptr_torture_preempt, preempt_task);
> >- torture_stop_kthread(hazptr_torture_writer, writer_task);
> >
> > if (reader_tasks) {
> > for (i = 0; i < nrealreaders; i++)
> >@@ -785,6 +788,14 @@ hazptr_torture_cleanup(void)
> > reader_tasks = NULL;
> > }
> >
> >+ /*
> >+ * Stop the writer only after do_pending, and do_pending only after
> >+ * the readers: the writer may be waiting on hazard pointers that
> >+ * readers deferred, which do_pending releases once they stopped.
> >+ */
> >+ torture_stop_kthread(hazptr_torture_do_pending, do_pending_task);
> >+ torture_stop_kthread(hazptr_torture_writer, writer_task);
> >+
> > torture_stop_kthread(hazptr_torture_stats, stats_task);
> >
> > /* Do torture-type-specific cleanup operations. */
>
>
> This looks okay to me.
Glad you like it, but I do need to remove the extraneous changes.
Something for later today. ;-)
Thanx, Paul
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-11-03 4:50 ` Paul E. McKenney
2026-11-03 11:36 ` Bradley Morgan
@ 2026-11-03 19:05 ` Andrea Parri
2026-11-03 20:44 ` Paul E. McKenney
1 sibling, 1 reply; 18+ messages in thread
From: Andrea Parri @ 2026-11-03 19:05 UTC (permalink / raw)
To: Paul E. McKenney
Cc: Andrea Parri, Mathieu Desnoyers, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Lai Jiangshan, Zqiang,
Gary Guo, Bradley Morgan, Kunwu Chan, rcu, lkmm, linux-kernel
On Mon, Nov 02, 2026 at 08:50:51PM -0800, Paul E. McKenney wrote:
[...]
> How about like this?
Works for me, thanks! With it and patches 2-4 on top, two 3-minute
PREEMPT runs with kthread_do_pending_ms=10000, which hung without a fix,
and a 30-minute PREEMPT run end with "End of test: SUCCESS" and with as
many hazard pointers released as were deferred. In one of the
kthread_do_pending_ms=10000 runs, the writer also stays in ->sync() long
enough for a writer-stall report 31 seconds after boot, and moves on by
46 seconds.
The variant from my previous reply, with the pass right after the
reader's loop, passed the same tests, but there is no need for it with
this version, so I will not send a v2.
A few small things below.
[...]
> hazptr_torture_cleanup() stops the kthreads in random order, which
The order is fixed: do_pending, preempt, the writer, then the readers.
> Therefore, make each reader clean up all deferred hazard pointers,
> which means that after the last reader finishes, there can no
> longer be any deferred hazard pointers, which means that the
> writer can terminate promptly.
With the pass after torture_kthread_stopping(), a reader cleans up only
once hazptr_torture_cleanup() stops it, so this also relies on stopping
the readers before the writer. Maybe worth a sentence here?
> hazard poiners.
s/poiners/pointers/
> from the pending lists as were deferred (reldefer: 983727 relundefer
> 983727).
These numbers come from v1's patch; with this version, the 30-minute
PREEMPT run gives reldefer: 657653 relundefer 657653.
[...]
> + /*
> + * Stop the writer only after do_pending, and do_pending only after
> + * the readers: the writer may be waiting on hazard pointers that
> + * readers deferred, which do_pending releases once they stopped.
> + */
do_pending no longer releases them; the readers do. Perhaps:
/*
* Stop the writer only after the readers: the writer may be
* waiting on hazard pointers that readers deferred, which each
* reader releases once it is stopped.
*/
Also, "hazptrtorture: Nest a second hazard pointer in readers" conflicts
with moving hazptr_torture_do_one_pending() up, as both add a function
after hazptr_torture_defer(); keeping both resolves it.
Thanks,
Andrea
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-11-03 19:05 ` Andrea Parri
@ 2026-11-03 20:44 ` Paul E. McKenney
2026-11-03 23:42 ` Andrea Parri
0 siblings, 1 reply; 18+ messages in thread
From: Paul E. McKenney @ 2026-11-03 20:44 UTC (permalink / raw)
To: Andrea Parri
Cc: Mathieu Desnoyers, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
On Tue, Nov 03, 2026 at 08:05:06PM +0100, Andrea Parri wrote:
> On Mon, Nov 02, 2026 at 08:50:51PM -0800, Paul E. McKenney wrote:
>
> [...]
>
> > How about like this?
>
> Works for me, thanks! With it and patches 2-4 on top, two 3-minute
> PREEMPT runs with kthread_do_pending_ms=10000, which hung without a fix,
> and a 30-minute PREEMPT run end with "End of test: SUCCESS" and with as
> many hazard pointers released as were deferred. In one of the
> kthread_do_pending_ms=10000 runs, the writer also stays in ->sync() long
> enough for a writer-stall report 31 seconds after boot, and moves on by
> 46 seconds.
>
> The variant from my previous reply, with the pass right after the
> reader's loop, passed the same tests, but there is no need for it with
> this version, so I will not send a v2.
Very good, thank you!
> A few small things below.
>
> [...]
>
> > hazptr_torture_cleanup() stops the kthreads in random order, which
>
> The order is fixed: do_pending, preempt, the writer, then the readers.
That was my first thought, but then I remembered that torture_init_begin()
sets fullstop to FULLSTOP_DONTSTOP, which causes torture_must_stop()
to return true, thus stopping all the hazptrtorture kthread at about
the same time in some random order.
By the time hazptr_torture_cleanup() gets around to invoking
torture_stop_kthread() for the various kthreads, the game is up.
> > Therefore, make each reader clean up all deferred hazard pointers,
> > which means that after the last reader finishes, there can no
> > longer be any deferred hazard pointers, which means that the
> > writer can terminate promptly.
>
> With the pass after torture_kthread_stopping(), a reader cleans up only
> once hazptr_torture_cleanup() stops it, so this also relies on stopping
> the readers before the writer. Maybe worth a sentence here?
Again, the readers are stopped at torture_cleanup_begin() time.
Along with all the other kthreads.
> > hazard poiners.
>
> s/poiners/pointers/
Good eyes! I will fix this on my next rebase.
> > from the pending lists as were deferred (reldefer: 983727 relundefer
> > 983727).
>
> These numbers come from v1's patch; with this version, the 30-minute
> PREEMPT run gives reldefer: 657653 relundefer 657653.
Will fix this as well on my next rebase, and again, thank you.
> [...]
>
> > + /*
> > + * Stop the writer only after do_pending, and do_pending only after
> > + * the readers: the writer may be waiting on hazard pointers that
> > + * readers deferred, which do_pending releases once they stopped.
> > + */
>
> do_pending no longer releases them; the readers do. Perhaps:
>
> /*
> * Stop the writer only after the readers: the writer may be
> * waiting on hazard pointers that readers deferred, which each
> * reader releases once it is stopped.
> */
I instead dropped the torture_stop_kthread() reordering from the most
recent version of this patch shown below. (Which still contains the
above typos, but could you please check it otherwise?)
> Also, "hazptrtorture: Nest a second hazard pointer in readers" conflicts
> with moving hazptr_torture_do_one_pending() up, as both add a function
> after hazptr_torture_defer(); keeping both resolves it.
I believe that I have resolved that correctly, but could you please
check v7.4-rc1..d0a4ec9443ee in my -rcu tree?
Thanx, Paul
------------------------------------------------------------------------
commit ba9a3e4d3b237a7e2f844bd9595a97876f36a427
Author: Andrea Parri <parri.andrea@gmail.com>
Date: Tue Oct 27 17:38:03 2026 +0100
hazptrtorture: Release all deferred hazard pointers before stopping
hazptrtorture can hang at shutdown when deferral is enabled, as in the
PREEMPT scenario: the writer stays in ->sync() and kthread_stop() on it
never returns, until kvm.sh kills QEMU:
[ 1800.899278] hazptr-torture: Stopping hazptr_torture_do_pending task
[ 1800.899447] hazptr-torture: Stopping hazptr_torture_writer task
[ 1812.713870] hazptr-torture: rtc: 00000000225df70a ver: 12436 HTWS_SYNC tfle: 0 rta: 12437 rtaf: 0 rtf: 12427 onoff: 48783/48783:48786/48786 3,254:3,280 611671:731494 (HZ=1000) acq: 5720353149 rel: 5720210186 acqirq: 571927 relirq: 714778 reldefer: 714139 relundefer 714114
[ 1812.713971] hazptr-torture: hazptr_torture_stats is stopping
Summary: Potential hang (QEMU killed)
!!! PID 1393523 hung at 1980 vs. 1800 seconds
hazptr_torture_cleanup() stops the kthreads in random order, which
means that there could be a reader thread still running after
the do_pending kthread does its last cleanup pass. If the writer
is still running at that point, any remaining deferred hazard
pointers will never be cleaned up and the writer can wait forever
if it is unlucky enough to be waiting on one of the deferred
hazard poiners.
Therefore, make each reader clean up all deferred hazard pointers,
which means that after the last reader finishes, there can no
longer be any deferred hazard pointers, which means that the
writer can terminate promptly.
With this change, a 30-minute PREEMPT run on x86_64 with 8 CPUs ends
with "End of test: SUCCESS" and with as many hazard pointers released
from the pending lists as were deferred (reldefer: 983727 relundefer
983727).
The hang was found with the help of an LLM.
[ paulmck: Make readers release deferred hazard pointers. ]
Fixes: f23d7b47ced9 ("hazptrtorture: Add kthread to release deferred hazard pointers")
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
index 7c8b5899fb011..23c2bf229014d 100644
--- a/kernel/rcu/hazptrtorture.c
+++ b/kernel/rcu/hazptrtorture.c
@@ -449,6 +449,28 @@ static void hazptr_torture_defer(struct hazptr_pending *hppp, struct torture_ran
atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_defer, raw_smp_processor_id()));
}
+/*
+ * Release the specified CPU's set of deferred/pending hazard pointers.
+ */
+static void hazptr_torture_do_one_pending(int cpu, struct torture_random_state *trsp)
+{
+ struct hazptr_pending *hppp;
+ struct hazptr_pending *hppp1;
+ struct llist_head *llhp;
+ struct llist_node *llnp;
+
+ llhp = per_cpu_ptr(&hazptr_pending, cpu);
+ llnp = llist_del_all(llhp);
+ if (!llnp)
+ return;
+ llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
+ hazptr_torture_reader_tail(hppp, trsp);
+ atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
+ raw_smp_processor_id()));
+ kfree(hppp);
+ }
+}
+
/*
* Hazard-pointer torture reader kthread. Repeatedly dereferences
* hazptr_torture_current, incrementing the corresponding element of the
@@ -513,31 +535,13 @@ static int hazptr_torture_reader(void *arg)
stutter_wait("hazptr_torture_reader");
} while (!torture_must_stop());
torture_kthread_stopping("hazptr_torture_reader");
+ // Each reader clears all CPUs, so nothing is left after all
+ // readers exit their for_each_possible_cpu() loops.
+ for_each_possible_cpu(cpu)
+ hazptr_torture_do_one_pending(cpu, &rand);
return 0;
}
-/*
- * Release the specified CPU's set of deferred/pending hazard pointers.
- */
-static void hazptr_torture_do_one_pending(int cpu, struct torture_random_state *trsp)
-{
- struct hazptr_pending *hppp;
- struct hazptr_pending *hppp1;
- struct llist_head *llhp;
- struct llist_node *llnp;
-
- llhp = per_cpu_ptr(&hazptr_pending, cpu);
- llnp = llist_del_all(llhp);
- if (!llnp)
- return;
- llist_for_each_entry_safe(hppp, hppp1, llnp, hpp_node) {
- hazptr_torture_reader_tail(hppp, trsp);
- atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_undefer,
- raw_smp_processor_id()));
- kfree(hppp);
- }
-}
-
/*
* Hazard-pointer release of deferred/pending hazard pointers.
*/
@@ -560,6 +564,7 @@ static int hazptr_torture_do_pending(void *arg)
// Omit stutter_wait() because this function needs to do cleanup.
} while (!torture_must_stop());
torture_kthread_stopping("hazptr_torture_do_pending");
+ // Readers clean up after themselves before exit.
return 0;
}
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-11-03 20:44 ` Paul E. McKenney
@ 2026-11-03 23:42 ` Andrea Parri
2026-11-03 23:56 ` Paul E. McKenney
0 siblings, 1 reply; 18+ messages in thread
From: Andrea Parri @ 2026-11-03 23:42 UTC (permalink / raw)
To: Paul E. McKenney
Cc: Andrea Parri, Mathieu Desnoyers, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Lai Jiangshan, Zqiang,
Gary Guo, Bradley Morgan, Kunwu Chan, rcu, lkmm, linux-kernel
On Tue, Nov 03, 2026 at 12:44:07PM -0800, Paul E. McKenney wrote:
> On Tue, Nov 03, 2026 at 08:05:06PM +0100, Andrea Parri wrote:
[...]
> > > hazptr_torture_cleanup() stops the kthreads in random order, which
> >
> > The order is fixed: do_pending, preempt, the writer, then the readers.
>
> That was my first thought, but then I remembered that torture_init_begin()
> sets fullstop to FULLSTOP_DONTSTOP, which causes torture_must_stop()
> to return true, thus stopping all the hazptrtorture kthread at about
> the same time in some random order.
>
> By the time hazptr_torture_cleanup() gets around to invoking
> torture_stop_kthread() for the various kthreads, the game is up.
torture_must_stop() does become true for all the kthreads at once,
when torture_cleanup_begin() sets FULLSTOP_RMMOD, but each, once out of
its loop, waits in torture_kthread_stopping() until
hazptr_torture_cleanup() calls kthread_stop() on it, in the order above.
[...]
> I instead dropped the torture_stop_kthread() reordering from the most
> recent version of this patch shown below. (Which still contains the
> above typos, but could you please check it otherwise?)
With the pass after torture_kthread_stopping(), each reader reaches that
pass only after kthread_stop() is called on it. Cleanup now waits for
the writer before making those calls:
/* hazptr_torture_cleanup(): */
torture_stop_kthread(hazptr_torture_writer, ...) /* waits */
torture_stop_kthread(hazptr_torture_reader, ...) /* releases */
So a writer waiting in ->sync() on a deferred hazard pointer left after
do_pending's last pass keeps kthread_stop() on it from returning, as in
the hang this patch fixes, and the readers are never stopped.
With ba9a3e4d3b237 and patches 2-4 on top (d0a4ec9443ee), both
3-minute PREEMPT runs with kthread_do_pending_ms=10000 hang this way:
after "Stopping hazptr_torture_writer task", the writer stays in
->sync(), no reader is stopped, and 3823 and 4072 deferred hazard
pointers are never released (reldefer - relundefer).
Keeping the reordering, or doing the pass right after the reader's
loop, before torture_kthread_stopping(), avoids this. Your previous
version, which keeps the reordering, passed those runs, as did the
variant from my earlier reply, which does both.
> > Also, "hazptrtorture: Nest a second hazard pointer in readers" conflicts
> > with moving hazptr_torture_do_one_pending() up, as both add a function
> > after hazptr_torture_defer(); keeping both resolves it.
>
> I believe that I have resolved that correctly, but could you please
> check v7.4-rc1..d0a4ec9443ee in my -rcu tree?
The resolution looks right to me: it keeps both functions, and the
patch adds and removes the same lines as in v1.
Thanks,
Andrea
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping
2026-11-03 23:42 ` Andrea Parri
@ 2026-11-03 23:56 ` Paul E. McKenney
0 siblings, 0 replies; 18+ messages in thread
From: Paul E. McKenney @ 2026-11-03 23:56 UTC (permalink / raw)
To: Andrea Parri
Cc: Mathieu Desnoyers, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
On Wed, Nov 04, 2026 at 12:42:41AM +0100, Andrea Parri wrote:
> On Tue, Nov 03, 2026 at 12:44:07PM -0800, Paul E. McKenney wrote:
> > On Tue, Nov 03, 2026 at 08:05:06PM +0100, Andrea Parri wrote:
>
> [...]
>
> > > > hazptr_torture_cleanup() stops the kthreads in random order, which
> > >
> > > The order is fixed: do_pending, preempt, the writer, then the readers.
> >
> > That was my first thought, but then I remembered that torture_init_begin()
> > sets fullstop to FULLSTOP_DONTSTOP, which causes torture_must_stop()
> > to return true, thus stopping all the hazptrtorture kthread at about
> > the same time in some random order.
> >
> > By the time hazptr_torture_cleanup() gets around to invoking
> > torture_stop_kthread() for the various kthreads, the game is up.
>
> torture_must_stop() does become true for all the kthreads at once,
> when torture_cleanup_begin() sets FULLSTOP_RMMOD, but each, once out of
> its loop, waits in torture_kthread_stopping() until
> hazptr_torture_cleanup() calls kthread_stop() on it, in the order above.
>
> [...]
Fair point, and so I have moved the hazptr_torture_do_one_pending()
loop to precede that call to torture_kthread_stopping().
> > I instead dropped the torture_stop_kthread() reordering from the most
> > recent version of this patch shown below. (Which still contains the
> > above typos, but could you please check it otherwise?)
>
> With the pass after torture_kthread_stopping(), each reader reaches that
> pass only after kthread_stop() is called on it. Cleanup now waits for
> the writer before making those calls:
>
> /* hazptr_torture_cleanup(): */
> torture_stop_kthread(hazptr_torture_writer, ...) /* waits */
> torture_stop_kthread(hazptr_torture_reader, ...) /* releases */
>
> So a writer waiting in ->sync() on a deferred hazard pointer left after
> do_pending's last pass keeps kthread_stop() on it from returning, as in
> the hang this patch fixes, and the readers are never stopped.
>
> With ba9a3e4d3b237 and patches 2-4 on top (d0a4ec9443ee), both
> 3-minute PREEMPT runs with kthread_do_pending_ms=10000 hang this way:
> after "Stopping hazptr_torture_writer task", the writer stays in
> ->sync(), no reader is stopped, and 3823 and 4072 deferred hazard
> pointers are never released (reldefer - relundefer).
>
> Keeping the reordering, or doing the pass right after the reader's
> loop, before torture_kthread_stopping(), avoids this. Your previous
> version, which keeps the reordering, passed those runs, as did the
> variant from my earlier reply, which does both.
And your first sentence does cover it. ;-)
Putting the hazptr_torture_do_one_pending() loop before the call to
torture_kthread_stopping() from hazptr_torture_reader() has less "spooky
action at a distance", so I am taking that approach.
Unless of course I am still missing something.
> > > Also, "hazptrtorture: Nest a second hazard pointer in readers" conflicts
> > > with moving hazptr_torture_do_one_pending() up, as both add a function
> > > after hazptr_torture_defer(); keeping both resolves it.
> >
> > I believe that I have resolved that correctly, but could you please
> > check v7.4-rc1..d0a4ec9443ee in my -rcu tree?
>
> The resolution looks right to me: it keeps both functions, and the
> patch adds and removes the same lines as in v1.
Whew!!!
Thanx, Paul
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH -rcu/dev 2/4] hazptr: Check per-CPU slot owner with CONFIG_HAZPTR_DEBUG
2026-10-27 16:38 [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race Andrea Parri
2026-10-27 16:38 ` [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping Andrea Parri
@ 2026-10-27 16:38 ` Andrea Parri
2026-10-27 16:38 ` [PATCH -rcu/dev 3/4] hazptr: Do not warn when a backup slot is released on another CPU Andrea Parri
` (2 subsequent siblings)
4 siblings, 0 replies; 18+ messages in thread
From: Andrea Parri @ 2026-10-27 16:38 UTC (permalink / raw)
To: Paul E. McKenney, Mathieu Desnoyers
Cc: Andrea Parri, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
When a per-CPU slot records the wrong hazptr_ctx as its owner, nothing
reports it where it happens. If the task is switched out before
releasing the slot, hazptr_note_context_switch() promotes it through the
wrong context, and if that context is detached,
hazptr_chain_backup_slot() adds its backup slot to an overflow list
again.
The kernel can then crash later in hazptr_synchronize(), far from the
cause. For example, from hazptrtorture with readers that nest a second
hazard pointer, as "hazptrtorture: Nest a second hazard pointer in
readers" makes them do, and without "hazptr: Record per-CPU slot owner
after claiming the slot":
Oops: stack segment: 0000 [#1] SMP PTI
CPU: 4 UID: 0 PID: 83 Comm: hazptr_torture_ Tainted: G W 7.3.0-rc1-00107-ga2aa5148421f #2 PREEMPTLAZY
RIP: 0010:hazptr_scan_overflow_list_period+0x94/0x100
RBP: dead000000000100 R08: 00000000002fa6bc R09: 0000000000000004
Call Trace:
<TASK>
hazptr_synchronize+0xb7/0xd0
hazptr_torture_writer+0x181/0x3f0
kthread+0xd1/0x100
ret_from_fork+0x179/0x240
ret_from_fork_asm+0x1a/0x30
</TASK>
Check the recorded owner where it is used. hazptr_release_debug() now
warns when the context releasing a per-CPU slot is not the one recorded
for it. hazptr_note_context_switch() warns when the recorded owner's
->slot is not the slot being promoted. In that run, the checks warned
1.3 and 0.08 seconds before the oops:
Hazard Pointer (addr=000000006dd14922) released from a per-CPU slot recorded with another owner. Acquire: caller=hazptr_torture_read_lock+0x43/0xb0, pid=82, cpu=7.
WARNING: ./include/linux/hazptr.h:304 at hazptr_torture_read_unlock+0x137/0x160, CPU#7: hazptr_torture_/82
Call Trace:
<TASK>
hazptr_torture_reader_tail+0x80/0x220
hazptr_torture_nested_reader+0x6c/0x90
hazptr_torture_reader+0x2f5/0x530
kthread+0xd1/0x100
ret_from_fork+0x179/0x240
ret_from_fork_asm+0x1a/0x30
</TASK>
Hazard Pointer (addr=00000000742d8665) in per-CPU slot 1 recorded with an owner that uses another slot.
WARNING: ./include/linux/hazptr.h:185 at __schedule+0x7ba/0xf10, CPU#0: hazptr_torture_/82
Call Trace:
<TASK>
preempt_schedule+0x2d/0x50
preempt_schedule_thunk+0x16/0x40
hazptr_torture_read_lock+0xaa/0xb0
hazptr_torture_nested_reader+0x39/0x90
hazptr_torture_reader+0x2f5/0x530
kthread+0xd1/0x100
ret_from_fork+0x179/0x240
ret_from_fork_asm+0x1a/0x30
</TASK>
hazptr_release_debug() reads the owner of ctx->slot only if that
per-CPU slot is in use, and skips the backup slot that a detached or
promoted ctx uses. If ctx holds the per-CPU slot, no interrupt can
claim it between the ->addr check and the owner read, so the check warns
only if the slot records the wrong owner.
If ctx has already released the slot, which the hazptr_release()
kernel-doc permits, the slot is in use only if another hazard pointer
has claimed it since, and this second release would clear that hazard
pointer, so the check warns then too.
One case can escape both checks: an interrupt handler that acquires and
releases the slot instead of detaching it, then a context switch before
the task's release. The stale owner's ->slot still names the slot, and
the promotion empties the task's per-CPU slot, so hazptr_release_debug()
warns only if another hazard pointer has claimed that slot by then.
The crash was found with the help of an LLM.
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
include/linux/hazptr.h | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/include/linux/hazptr.h b/include/linux/hazptr.h
index 90d8d5a3d2443..d37792af34939 100644
--- a/include/linux/hazptr.h
+++ b/include/linux/hazptr.h
@@ -182,6 +182,9 @@ void hazptr_note_context_switch(void)
if (!slot->addr)
continue;
#ifdef CONFIG_HAZPTR_DEBUG
+ WARN_ONCE(item->ctx.ctx->slot != slot,
+ "Hazard Pointer (addr=%p) in per-CPU slot %u recorded with an owner that uses another slot.",
+ slot->addr, idx);
item->ctx.ctx->detach_cpu = true;
#endif
hazptr_promote_to_backup_slot(item->ctx.ctx, slot);
@@ -284,6 +287,8 @@ void hazptr_release_debug(struct hazptr_ctx *ctx, void *addr)
int pid = current->pid, cpu = smp_processor_id();
bool warn_remote_cpu = !ctx->detach_cpu && ctx->acquire_cpu != cpu,
warn_remote_task = !ctx->detach_task && ctx->acquire_pid != pid;
+ struct hazptr_slot *slot = ctx->slot;
+ struct hazptr_slot_item *item;
WARN_ONCE(warn_remote_cpu || warn_remote_task,
"Hazard Pointer (addr=%p) released on remote %s without %s. Acquire: caller=%pS, pid=%d, cpu=%d. Release: pid=%d, cpu=%d.",
@@ -291,6 +296,14 @@ void hazptr_release_debug(struct hazptr_ctx *ctx, void *addr)
warn_remote_task ? "task" : "cpu",
warn_remote_task ? "being detached from task" : "context switch",
(void *) ctx->acquire_caller, ctx->acquire_pid, ctx->acquire_cpu, pid, cpu);
+
+ if (hazptr_slot_is_backup(ctx, slot) || !READ_ONCE(slot->addr))
+ return;
+ /* Whoever claimed a per-CPU slot must be recorded as its owner. */
+ item = container_of(slot, struct hazptr_slot_item, slot);
+ WARN_ONCE(item->ctx.ctx != ctx,
+ "Hazard Pointer (addr=%p) released from a per-CPU slot recorded with another owner. Acquire: caller=%pS, pid=%d, cpu=%d.",
+ addr, (void *) ctx->acquire_caller, ctx->acquire_pid, ctx->acquire_cpu);
}
#else
static inline void hazptr_release_debug(struct hazptr_ctx *ctx, void *addr) { }
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH -rcu/dev 3/4] hazptr: Do not warn when a backup slot is released on another CPU
2026-10-27 16:38 [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race Andrea Parri
2026-10-27 16:38 ` [PATCH -rcu/dev 1/4] hazptrtorture: Release all deferred hazard pointers before stopping Andrea Parri
2026-10-27 16:38 ` [PATCH -rcu/dev 2/4] hazptr: Check per-CPU slot owner with CONFIG_HAZPTR_DEBUG Andrea Parri
@ 2026-10-27 16:38 ` Andrea Parri
2026-10-28 17:30 ` Bradley Morgan
2026-10-27 16:38 ` [PATCH -rcu/dev 4/4] hazptrtorture: Nest a second hazard pointer in readers Andrea Parri
2026-10-29 9:58 ` [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race KunWu Chan
4 siblings, 1 reply; 18+ messages in thread
From: Andrea Parri @ 2026-10-27 16:38 UTC (permalink / raw)
To: Paul E. McKenney, Mathieu Desnoyers
Cc: Andrea Parri, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
With CONFIG_HAZPTR_DEBUG, hazptr_release_debug() warns when a hazard
pointer is released on a CPU other than the one that acquired it,
unless ->detach_cpu is set. hazptr_detach() sets it, and so does
hazptr_note_context_switch() when it promotes a per-CPU slot to the
backup slot.
__hazptr_acquire() can also put the hazard pointer in the backup slot
directly, when all per-CPU slots are in use or the pointer changes
under __hazptr_try_acquire(). Nothing sets ->detach_cpu then, so if
the task is migrated before the release, the check warns although the
backup slot can be released on any CPU.
From hazptrtorture, with CPU hotplug and readers that nest a second
hazard pointer, as "hazptrtorture: Nest a second hazard pointer in
readers" makes them do (without the nesting, 30-minute PREEMPT and
NOPREEMPT runs do not hit it):
Hazard Pointer (addr=00000000e2979b1e) released on remote cpu without context switch. Acquire: caller=hazptr_torture_read_lock+0x43/0xb0, pid=80, cpu=0. Release: pid=80, cpu=1.
WARNING: ./include/linux/hazptr.h:293 at hazptr_torture_read_unlock+0x80/0x160, CPU#1: hazptr_torture_/80
Call Trace:
<TASK>
hazptr_torture_reader_tail+0x88/0x250
hazptr_torture_nested_reader+0x6c/0x90
hazptr_torture_reader+0x2f5/0x550
kthread+0xdf/0x110
ret_from_fork+0x179/0x240
ret_from_fork_asm+0x1a/0x30
</TASK>
Set ->detach_cpu when __hazptr_acquire() falls back to the backup slot.
The remote-task check still applies to such a hazard pointer.
The false positive was found with the help of an LLM.
Fixes: 3d48872eb74f ("hazptr: Introduce CONFIG_HAZPTR_DEBUG misuse detection")
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
kernel/hazptr.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/kernel/hazptr.c b/kernel/hazptr.c
index b15b84ebd4ceb..b06164aac3a70 100644
--- a/kernel/hazptr.c
+++ b/kernel/hazptr.c
@@ -112,6 +112,10 @@ void *__hazptr_acquire(struct hazptr_ctx *ctx, void * const *addr_p)
* synchronize forward progress.
*/
slot = hazptr_chain_backup_slot(ctx);
+#ifdef CONFIG_HAZPTR_DEBUG
+ /* Unlike a per-CPU slot, the backup slot can be released on any CPU. */
+ ctx->detach_cpu = true;
+#endif
WRITE_ONCE(slot->addr, HAZPTR_WILDCARD); /* Store B */
/* Memory ordering: Store B before Load A. */
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 3/4] hazptr: Do not warn when a backup slot is released on another CPU
2026-10-27 16:38 ` [PATCH -rcu/dev 3/4] hazptr: Do not warn when a backup slot is released on another CPU Andrea Parri
@ 2026-10-28 17:30 ` Bradley Morgan
0 siblings, 0 replies; 18+ messages in thread
From: Bradley Morgan @ 2026-10-28 17:30 UTC (permalink / raw)
To: Andrea Parri, Paul E. McKenney, Mathieu Desnoyers
Cc: Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Boqun Feng, Uladzislau Rezki, Steven Rostedt,
Lai Jiangshan, Zqiang, Gary Guo, Kunwu Chan, rcu, lkmm,
linux-kernel
On 27 October 2026 16:38:05 GMT, Andrea Parri <parri.andrea@gmail.com>
wrote:
>With CONFIG_HAZPTR_DEBUG, hazptr_release_debug() warns when a hazard
>pointer is released on a CPU other than the one that acquired it,
>unless ->detach_cpu is set. hazptr_detach() sets it, and so does
>hazptr_note_context_switch() when it promotes a per-CPU slot to the
>backup slot.
>
>__hazptr_acquire() can also put the hazard pointer in the backup slot
>directly, when all per-CPU slots are in use or the pointer changes
>under __hazptr_try_acquire(). Nothing sets ->detach_cpu then, so if
>the task is migrated before the release, the check warns although the
>backup slot can be released on any CPU.
>
>From hazptrtorture, with CPU hotplug and readers that nest a second
>hazard pointer, as "hazptrtorture: Nest a second hazard pointer in
>readers" makes them do (without the nesting, 30-minute PREEMPT and
>NOPREEMPT runs do not hit it):
>
> Hazard Pointer (addr=00000000e2979b1e) released on remote cpu without context switch. Acquire: caller=hazptr_torture_read_lock+0x43/0xb0, pid=80, cpu=0. Release: pid=80, cpu=1.
> WARNING: ./include/linux/hazptr.h:293 at hazptr_torture_read_unlock+0x80/0x160, CPU#1: hazptr_torture_/80
> Call Trace:
> <TASK>
> hazptr_torture_reader_tail+0x88/0x250
> hazptr_torture_nested_reader+0x6c/0x90
> hazptr_torture_reader+0x2f5/0x550
> kthread+0xdf/0x110
> ret_from_fork+0x179/0x240
> ret_from_fork_asm+0x1a/0x30
> </TASK>
>
>Set ->detach_cpu when __hazptr_acquire() falls back to the backup slot.
>The remote-task check still applies to such a hazard pointer.
>
>The false positive was found with the help of an LLM.
>
>Fixes: 3d48872eb74f ("hazptr: Introduce CONFIG_HAZPTR_DEBUG misuse detection")
Reviewed-by: Bradley Morgan <brads@mainlining.org>
>Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
>---
> kernel/hazptr.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
>diff --git a/kernel/hazptr.c b/kernel/hazptr.c
>index b15b84ebd4ceb..b06164aac3a70 100644
>--- a/kernel/hazptr.c
>+++ b/kernel/hazptr.c
>@@ -112,6 +112,10 @@ void *__hazptr_acquire(struct hazptr_ctx *ctx, void * const *addr_p)
> * synchronize forward progress.
> */
> slot = hazptr_chain_backup_slot(ctx);
>+#ifdef CONFIG_HAZPTR_DEBUG
>+ /* Unlike a per-CPU slot, the backup slot can be released on any CPU. */
>+ ctx->detach_cpu = true;
>+#endif
Makes enough sense.
> WRITE_ONCE(slot->addr, HAZPTR_WILDCARD); /* Store B */
>
> /* Memory ordering: Store B before Load A. */
>
--- Thanks!
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH -rcu/dev 4/4] hazptrtorture: Nest a second hazard pointer in readers
2026-10-27 16:38 [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race Andrea Parri
` (2 preceding siblings ...)
2026-10-27 16:38 ` [PATCH -rcu/dev 3/4] hazptr: Do not warn when a backup slot is released on another CPU Andrea Parri
@ 2026-10-27 16:38 ` Andrea Parri
2026-10-29 9:58 ` [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race KunWu Chan
4 siblings, 0 replies; 18+ messages in thread
From: Andrea Parri @ 2026-10-27 16:38 UTC (permalink / raw)
To: Paul E. McKenney, Mathieu Desnoyers
Cc: Andrea Parri, Frederic Weisbecker, Neeraj Upadhyay,
Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
Steven Rostedt, Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan,
Kunwu Chan, rcu, lkmm, linux-kernel
hazptrtorture rarely exercises the race fixed by "hazptr: Record per-CPU
slot owner after claiming the slot": an interrupt handler acquiring a
hazard pointer after the interrupted task chooses a slot in
__hazptr_acquire() and before it claims it. Readers get there only when
their pointer is NULL or changes under them.
This commit therefore adds the nest_acquire module parameter, which
makes a reader acquire and release a second hazard pointer while holding
the first, once per the specified number of acquisitions on average
(default two, zero disables). While the outer hazard pointer is in
slot 0 of the reader's CPU, the nested acquisition finds that slot in
use and goes to __hazptr_acquire(), where hazptr_torture_acquire()
handlers from other readers can interrupt it.
Without that fix, and with the checks added by "hazptr: Check per-CPU
slot owner with CONFIG_HAZPTR_DEBUG", the PREEMPT and NOPREEMPT
scenarios at default parameters report the race 6.3 and 6.0 seconds
after boot, and NOPREEMPT oopses 1.3 seconds later. With
nest_acquire=0, both run 10 minutes without a report.
With the fix, 30-minute runs of both scenarios pass, as does a 10-minute
PREEMPT run with irq_acquire=100 and nest_acquire=1.
The nesting costs about a third of the outer reader iterations, as seen
in the IPI acquisitions, which readers send once per irq_acquire
iterations. PREEMPT counts acqirq: 270645 in 10 minutes without nesting
and acqirq: 544976 in 30 minutes with it.
Signed-off-by: Andrea Parri <parri.andrea@gmail.com>
---
.../admin-guide/kernel-parameters.txt | 6 +++++
kernel/rcu/hazptrtorture.c | 27 +++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index e3c5a6b68fc34..63bb7719457dd 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1992,6 +1992,12 @@ Kernel parameters
is equal to zero, in which case it instead
defaults to zero (disabled).
+ hazptrtorture.nest_acquire= [KNL]
+ Acquire a second, nested hazard pointer on
+ average one out of the specified number of
+ reader acquisitions. Zero disables. Defaults
+ to two.
+
hazptrtorture.nreaders= [KNL]
Number of hazard-pointer reader kthreads, each
of which repeatedly acquires and releases hazard
diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
index c46083a8af093..4373bc5fe11e5 100644
--- a/kernel/rcu/hazptrtorture.c
+++ b/kernel/rcu/hazptrtorture.c
@@ -37,6 +37,8 @@ torture_param(int, irq_release, -1,
"Release hazard pointers from irq handlers once per specified #, zero to disable");
torture_param(int, kthread_do_pending_ms, -1,
"Delay between cleanups for deferred hazard pointers (ms), zero to disable");
+torture_param(int, nest_acquire, 2,
+ "Nest a second hazard pointer once per specified # of reader acquires, zero to disable");
torture_param(int, nreaders, -1, "Number of hazard-pointer reader threads");
torture_param(bool, nwriters, 1, "Number of hazard-pointer writer threads, 0 or 1");
torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)");
@@ -449,6 +451,22 @@ static void hazptr_torture_defer(struct hazptr_pending *hppp, struct torture_ran
atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_defer, raw_smp_processor_id()));
}
+/*
+ * Acquire and release a second hazard pointer while the caller holds one.
+ * If the caller's hazard pointer is in this CPU's slot 0, this acquisition
+ * takes the slow path, which first tries another per-CPU slot and can race
+ * there with hazptr_torture_acquire() handlers.
+ */
+static void hazptr_torture_nested_reader(struct torture_random_state *trsp)
+{
+ struct hazptr_pending hpp;
+
+ hpp.hpp_htp = cur_ops->readlock(&hpp.hpp_hc);
+ atomic_long_inc(per_cpu_ptr(&hazptr_torture_acquires, raw_smp_processor_id()));
+ if (hpp.hpp_htp)
+ hazptr_torture_reader_tail(&hpp, trsp);
+}
+
/*
* Hazard-pointer torture reader kthread. Repeatedly dereferences
* hazptr_torture_current, incrementing the corresponding element of the
@@ -497,6 +515,8 @@ static int hazptr_torture_reader(void *arg)
schedule_timeout_interruptible(HZ / 10);
continue;
}
+ if (nest_acquire && !(torture_random(&rand) % nest_acquire))
+ hazptr_torture_nested_reader(&rand);
if (time_after(jiffies, lastsleep) && !torture_must_stop()) {
torture_hrtimeout_us(500, 1000, &rand);
lastsleep = jiffies + 10;
@@ -721,6 +741,7 @@ hazptr_torture_print_module_parms(struct hazptr_torture_ops *cur_ops, const char
pr_alert("%s" TORTURE_FLAG
"--- %s: nreaders=%d nwriters=%d "
"defer_modulus=%d irq_acquire=%d irq_release=%d kthread_do_pending_ms=%d "
+ "nest_acquire=%d "
"onoff_interval=%d onoff_holdoff=%d "
"preempt_duration=%d preempt_interval=%d "
"reader_sleep_us=%d "
@@ -728,6 +749,7 @@ hazptr_torture_print_module_parms(struct hazptr_torture_ops *cur_ops, const char
"verbose=%d\n",
torture_type, tag, nrealreaders, nwriters,
defer_modulus, irq_acquire, irq_release, kthread_do_pending_ms,
+ nest_acquire,
onoff_interval, onoff_holdoff,
preempt_duration, preempt_interval,
reader_sleep_us,
@@ -891,6 +913,11 @@ static int __init hazptr_torture_init(void)
WARN_ON(IS_BUILTIN(CONFIG_HAZPTR_TORTURE_TEST));
irq_release = 0;
}
+ if (nest_acquire < 0) {
+ pr_alert("Cannot have negative nest_acquire (%d), disabling.\n", nest_acquire);
+ WARN_ON(IS_BUILTIN(CONFIG_HAZPTR_TORTURE_TEST));
+ nest_acquire = 0;
+ }
reader_tasks = kzalloc_objs(reader_tasks[0], nrealreaders);
for (i = 0; i < nrealreaders; i++) {
firsterr = torture_create_kthread(hazptr_torture_reader, (void *)i,
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race
2026-10-27 16:38 [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race Andrea Parri
` (3 preceding siblings ...)
2026-10-27 16:38 ` [PATCH -rcu/dev 4/4] hazptrtorture: Nest a second hazard pointer in readers Andrea Parri
@ 2026-10-29 9:58 ` KunWu Chan
2026-10-29 14:48 ` Andrea Parri
4 siblings, 1 reply; 18+ messages in thread
From: KunWu Chan @ 2026-10-29 9:58 UTC (permalink / raw)
To: Andrea Parri
Cc: Paul E. McKenney, Mathieu Desnoyers, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Lai Jiangshan, Zqiang,
Gary Guo, Bradley Morgan, rcu, lkmm, linux-kernel
I ran into both issues independently while developing new shared-scan
hazptr series and its torture-test scenarios.
For patch 1, I reproduced a shutdown hang on 16-CPU ARM64 KVM with
`CONFIG_HAZPTR_ACQUIRE_FORCE_SLOWPATH=y`, `nreaders=16`,
and `defer_modulus=4000`. The readers remained in D state, with
deferred-context stall reports reaching 147 seconds.
My prototype uses the same all-CPU drain operation, but keeps the writer
ahead of the readers in the cleanup order. With that change, the same test
unloaded in seconds. The upstream configuration was harder to trigger; I did
not reproduce the hang in a 3-minute run on the unmodified `origin/dev`
baseline with `defer_modulus=100`.
Paul's concern about `torture_must_stop()` is valid: `fullstop` can make the
drainer exit before the readers have stopped, so its exit-time flush is not
necessarily final. The reader-exit approach looks worth testing against both
the forced-slowpath case and the PREEMPT scenario Andrea reported.
I also saw the patch-3 warning during ARM64 `PROVE_LOCKING` testing,
although I have not reproduced it consistently. The warning count dropped
from 3 to 0 when the relevant fixes were applied together, so I cannot attribute
that change to patch 3 alone.
These tests independently reproduced both issues in my setup. I'll check the
final cleanup approach against the forced-slowpath scenario as the
series settles.
Thanks,
Kunwu
On Wed, Oct 28, 2026 at 12:38 AM Andrea Parri <parri.andrea@gmail.com> wrote:
>
> Patch 1 fixes a shutdown hang in hazptrtorture with deferral enabled, as
> in the PREEMPT scenario: hazptr_torture_cleanup() stops the kthread that
> releases deferred hazard pointers before the writer, which can then
> wait in ->sync() forever. It now stops the readers before that
> kthread, which releases what is left once the readers have stopped, and
> that kthread before the writer.
>
> Patches 2 and 4 make hazptrtorture catch the race fixed by "hazptr:
> Record per-CPU slot owner after claiming the slot" (now on -rcu/dev)
> should it come back, as Paul asked [1] and agreed to [2]. Patch 2
> adds two CONFIG_HAZPTR_DEBUG checks of the recorded slot owner, in
> hazptr_release_debug() and hazptr_note_context_switch(). Patch 4 adds
> the nest_acquire module parameter, which makes readers take a second,
> nested hazard pointer that goes through __hazptr_acquire() while the
> first is in slot 0.
>
> Patch 3 fixes a false positive of an existing CONFIG_HAZPTR_DEBUG
> check, which the nesting of patch 4 exposes with CPU hotplug: a hazard
> pointer that __hazptr_acquire() puts directly in the backup slot warns
> if the task migrates before releasing it. It comes before patch 4,
> which would otherwise make runs with CPU hotplug warn.
>
> The series applies on -rcu/dev. 30-minute hazptrtorture runs of the
> PREEMPT and NOPREEMPT scenarios pass on x86_64 with 8 CPUs, as do a
> 30-minute NOPREEMPT run with torture_type=hazptr and three 10-minute
> PREEMPT runs: one with irq_acquire=100 and nest_acquire=1, one with
> CONFIG_HAZPTR_DEBUG=n, and one with torture_type=hazptr-stack. The
> 30-minute PREEMPT run ends with as many hazard pointers released from
> the pending lists as were deferred (reldefer: 650385 relundefer
> 650385). With kthread_do_pending_ms=10000, two 3-minute PREEMPT runs
> shut down in 100 and 130ms, well within one period.
>
> Without patch 4, hazptrtorture passes even without the slot-owner fix:
> its readers hold one hazard pointer each, so they reach the slow path
> only when the ptr_eq() check fails or the pointer is NULL. With
> patches 2 and 4 but without the fix, PREEMPT and NOPREEMPT report the
> race 6.3 and 6.0 seconds after boot, and with nest_acquire=0 both run
> 10 minutes without a report.
>
> The nesting costs about a third of the outer reader iterations at the
> default nest_acquire=2. If that is too much for every run, it could
> default to zero and be enabled from the scenario files instead.
>
> Patches 2 and 4 stand in for the KUnit reproducer mentioned in the RFC
> notes of the fix, which needs a test-only hook in
> __hazptr_try_acquire(). The checks can miss one case that the KUnit
> test covers: an interrupt handler that acquires and releases the slot
> instead of detaching it, then a context switch before the task's
> release, when no other hazard pointer claims the slot in between.
> hazptrtorture does not produce that case either, as
> hazptr_torture_acquire() always detaches.
>
> Patch 2's release check also warns about a second hazptr_release() of
> a ctx whose per-CPU slot another hazard pointer has claimed since. The
> hazptr_release() kernel-doc permits that second release, but it clears
> the other hazard pointer, and a second release of a backup slot oopses
> even without this series. Nothing in-tree releases a hazard pointer
> twice. I will follow up separately on the kernel-doc.
>
> An LLM found the hang fixed by patch 1, the crash shown in patch 2 and
> the false positive fixed by patch 3. It also helped write all four
> patches and their changelogs, and ran the tests above.
>
> ---
> Changes since RFC:
> - Patch 1: stop the readers, then do_pending, then the writer, and
> release all deferred hazard pointers after torture_kthread_stopping(),
> instead of keeping do_pending in its loop until it is stopped (Paul).
> Paul suggested changing the "while (!torture_must_stop())" condition
> to "while (!kthread_should_stop())"; this keeps it, for the reason in
> [3]. The changelog also says when kthread_stop() can still delay the
> exit of do_pending by a full kthread_do_pending_ms sleep (Paul).
> Bradley's Acked-by is not carried over, as the patch changed.
> - Post patch 1 in a series (Bradley), now with patches 2 and 4, so
> that hazptrtorture catches the race fixed by the slot-owner RFC patch
> (Paul).
> - Patch 3: new, for the false positive that patch 4 exposes.
> - Do not repost the slot-owner RFC patch, which Paul has queued.
> - Drop the Assisted-by tags, following mainline commit 1e035297ffda
> ("docs: remove Assisted-by references"), and describe the LLM use
> in the changelogs and above instead.
>
> [1] https://lore.kernel.org/r/fa16d2b1-594e-494b-8cfc-9b8c13f2c223@paulmck-laptop/
> [2] https://lore.kernel.org/r/e0145c14-9d6a-4c91-816c-57dabd98879b@paulmck-laptop/
> [3] https://lore.kernel.org/r/20261023120755.2463384-1-parri.andrea@gmail.com/
>
> RFC patch 1: https://lore.kernel.org/r/20261019162005.12888-1-parri.andrea@gmail.com/
> RFC slot-owner fix: https://lore.kernel.org/r/20261019161852.12783-1-parri.andrea@gmail.com/
>
> Andrea Parri (4):
> hazptrtorture: Release all deferred hazard pointers before stopping
> hazptr: Check per-CPU slot owner with CONFIG_HAZPTR_DEBUG
> hazptr: Do not warn when a backup slot is released on another CPU
> hazptrtorture: Nest a second hazard pointer in readers
>
> .../admin-guide/kernel-parameters.txt | 6 +++
> include/linux/hazptr.h | 13 ++++++
> kernel/hazptr.c | 4 ++
> kernel/rcu/hazptrtorture.c | 40 ++++++++++++++++++-
> 4 files changed, 61 insertions(+), 2 deletions(-)
>
>
> base-commit: 633f91e3408cc65a5237752a3265b031c389e43e
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race
2026-10-29 9:58 ` [PATCH -rcu/dev 0/4] hazptrtorture: Fix shutdown hang and catch slot owner race KunWu Chan
@ 2026-10-29 14:48 ` Andrea Parri
0 siblings, 0 replies; 18+ messages in thread
From: Andrea Parri @ 2026-10-29 14:48 UTC (permalink / raw)
To: KunWu Chan
Cc: Andrea Parri, Paul E. McKenney, Mathieu Desnoyers,
Frederic Weisbecker, Neeraj Upadhyay, Joel Fernandes,
Josh Triplett, Boqun Feng, Uladzislau Rezki, Steven Rostedt,
Lai Jiangshan, Zqiang, Gary Guo, Bradley Morgan, rcu, lkmm,
linux-kernel
On Thu, Oct 29, 2026 at 05:58:27PM +0800, KunWu Chan wrote:
> I ran into both issues independently while developing new shared-scan
> hazptr series and its torture-test scenarios.
Thank you for testing these, and for the arm64 data points!
[...]
> Paul's concern about `torture_must_stop()` is valid: `fullstop` can make the
> drainer exit before the readers have stopped, so its exit-time flush is not
> necessarily final. The reader-exit approach looks worth testing against both
> the forced-slowpath case and the PREEMPT scenario Andrea reported.
do_pending does leave its loop then, but v1's for_each_possible_cpu()
pass comes after torture_kthread_stopping(), which returns only once
hazptr_torture_cleanup() stops do_pending, after the readers. Either
way, I have been testing Paul's suggestion of a pass after each
reader's loop, without v1's pass in do_pending.
[...]
> These tests independently reproduced both issues in my setup. I'll check the
> final cleanup approach against the forced-slowpath scenario as the
> series settles.
That would be very helpful, thank you.
Thanks,
Andrea
^ permalink raw reply [flat|nested] 18+ messages in thread