* [PATCH RFC 0/4] Hazard-pointer torture test
@ 2026-05-07 16:50 Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 1/4] torture: Add a hazptrtorture.c " Paul E. McKenney
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Paul E. McKenney @ 2026-05-07 16:50 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Mathieu Desnoyers
Hello!
This RFC series is the beginnings of a hazard-pointer torture-test module.
1. Add a hazptrtorture.c torture test.
2. Add testing of on-stack hazptr_ctx structures.
3. Add microsecond-scale sleep in readers.
4. Enable system-independent CPU overcommit.
Future extensions include cross-kthread and cross-context hazard-pointer
acquisition/release, along with who knows what all else.
Thanx, Paul
------------------------------------------------------------------------
b/include/linux/torture.h | 2
b/kernel/rcu/Kconfig.debug | 12
b/kernel/rcu/Makefile | 1
b/kernel/rcu/hazptrtorture.c | 684 ++++++++++
b/kernel/rcu/update.c | 3
b/tools/testing/selftests/rcutorture/bin/kvm.sh | 6
b/tools/testing/selftests/rcutorture/configs/hazptr/CFLIST | 2
b/tools/testing/selftests/rcutorture/configs/hazptr/CFcommon | 2
b/tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT | 17
b/tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT.boot | 1
b/tools/testing/selftests/rcutorture/configs/hazptr/PREEMPT | 14
b/tools/testing/selftests/rcutorture/configs/hazptr/ver_functions.sh | 40
kernel/rcu/hazptrtorture.c | 55
13 files changed, 823 insertions(+), 16 deletions(-)
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH RFC 1/4] torture: Add a hazptrtorture.c torture test
2026-05-07 16:50 [PATCH RFC 0/4] Hazard-pointer torture test Paul E. McKenney
@ 2026-05-07 16:51 ` Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 2/4] hazptrtorture: Add testing of on-stack hazptr_ctx structures Paul E. McKenney
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Paul E. McKenney @ 2026-05-07 16:51 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Mathieu Desnoyers,
Paul E. McKenney
This commit adds a torture test for hazard pointers.
[ paulmck: Apply kernel test robot feedback. ]
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
include/linux/torture.h | 2 +-
kernel/rcu/Kconfig.debug | 12 +
kernel/rcu/Makefile | 1 +
kernel/rcu/hazptrtorture.c | 684 ++++++++++++++++++
kernel/rcu/update.c | 3 +-
tools/testing/selftests/rcutorture/bin/kvm.sh | 6 +-
.../rcutorture/configs/hazptr/CFLIST | 2 +
.../rcutorture/configs/hazptr/CFcommon | 2 +
.../rcutorture/configs/hazptr/NOPREEMPT | 17 +
.../rcutorture/configs/hazptr/PREEMPT | 14 +
.../configs/hazptr/ver_functions.sh | 40 +
11 files changed, 778 insertions(+), 5 deletions(-)
create mode 100644 kernel/rcu/hazptrtorture.c
create mode 100644 tools/testing/selftests/rcutorture/configs/hazptr/CFLIST
create mode 100644 tools/testing/selftests/rcutorture/configs/hazptr/CFcommon
create mode 100644 tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT
create mode 100644 tools/testing/selftests/rcutorture/configs/hazptr/PREEMPT
create mode 100644 tools/testing/selftests/rcutorture/configs/hazptr/ver_functions.sh
diff --git a/include/linux/torture.h b/include/linux/torture.h
index 1b59056c3b1822..d80f24ff69e3e1 100644
--- a/include/linux/torture.h
+++ b/include/linux/torture.h
@@ -130,7 +130,7 @@ void _torture_stop_kthread(char *m, struct task_struct **tp);
#define torture_preempt_schedule() do { } while (0)
#endif
-#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST) || IS_ENABLED(CONFIG_LOCK_TORTURE_TEST) || IS_MODULE(CONFIG_LOCK_TORTURE_TEST)
+#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_ENABLED(CONFIG_LOCK_TORTURE_TEST) || IS_ENABLED(CONFIG_HAZPTR_TORTURE_TEST)
long torture_sched_setaffinity(pid_t pid, const struct cpumask *in_mask, bool dowarn);
#endif
diff --git a/kernel/rcu/Kconfig.debug b/kernel/rcu/Kconfig.debug
index 83ac4e82cad7ee..7629c345b0b68f 100644
--- a/kernel/rcu/Kconfig.debug
+++ b/kernel/rcu/Kconfig.debug
@@ -113,6 +113,18 @@ config RCU_REF_SCALE_TEST
Say M if you want to build it as a module instead.
Say N if you are unsure.
+config HAZPTR_TORTURE_TEST
+ tristate "Torture tests for hazard pointers"
+ depends on DEBUG_KERNEL
+ select TORTURE_TEST
+ default n
+ help
+ This option provides in-kernel hazard-pointer stress tests.
+
+ Say Y here if you want hazard-pointer testing built into the kernel.
+ Say M if you want to build them as a module instead.
+ Say N if you are unsure.
+
config REPRO_TEST
tristate "Bug-reproducibility kernel code"
depends on DEBUG_KERNEL
diff --git a/kernel/rcu/Makefile b/kernel/rcu/Makefile
index c97351ec679adc..12bddae9dd266e 100644
--- a/kernel/rcu/Makefile
+++ b/kernel/rcu/Makefile
@@ -10,6 +10,7 @@ endif
obj-y += update.o sync.o
obj-$(CONFIG_TREE_SRCU) += srcutree.o
obj-$(CONFIG_TINY_SRCU) += srcutiny.o
+obj-$(CONFIG_HAZPTR_TORTURE_TEST) += hazptrtorture.o
obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
obj-$(CONFIG_RCU_SCALE_TEST) += rcuscale.o
obj-$(CONFIG_RCU_REF_SCALE_TEST) += refscale.o
diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
new file mode 100644
index 00000000000000..1949a8da4f8c9d
--- /dev/null
+++ b/kernel/rcu/hazptrtorture.c
@@ -0,0 +1,684 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hazard-pointer module-based torture test facility
+ *
+ * Copyright (c) 2026 Meta Platforms, Inc. and affiliates.
+ *
+ * Author: Paul E. McKenney <paulmck@kernel.org>
+ */
+
+#define pr_fmt(fmt) fmt
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched/debug.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/torture.h>
+#include <linux/hazptr.h>
+#include <linux/rcupdate.h>
+
+#include "rcu.h"
+
+MODULE_DESCRIPTION("Hazard-pointer module-based torture test facility");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Paul E. McKenney <paulmckrcu@meta.com>");
+
+torture_param(int, irqreader, 1, "Allow hazard-pointer readers from irq handlers");
+// @@@ torture_param(int, leakpointer, 0, "Leak pointer dereferences from readers");
+torture_param(int, nreaders, -1, "Number of hazard-pointer reader threads");
+torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)");
+torture_param(int, onoff_interval, 0, "Time between CPU hotplugs (jiffies), 0=disable");
+// @@@ Move the rcu_torture_preempt() function and friends to kernel/torture.c.
+torture_param(int, preempt_duration, 0, "Preemption duration (ms), zero to disable");
+torture_param(int, preempt_interval, MSEC_PER_SEC, "Interval between preemptions (ms)");
+torture_param(int, shuffle_interval, 3, "Number of seconds between shuffles");
+torture_param(int, shutdown_secs, 0, "Shutdown time (s), <= zero to disable.");
+torture_param(int, stat_interval, 60, "Number of seconds between stats printk()s");
+torture_param(int, stutter, 5, "Number of seconds to run/halt test");
+torture_param(int, verbose, 1, "Enable verbose debugging printk()s");
+
+static char *torture_type = "hazptr";
+module_param(torture_type, charp, 0444);
+MODULE_PARM_DESC(torture_type, "Type of hazard pointers to torture (hazptr, ...)");
+
+static int nrealreaders;
+static struct task_struct *writer_task;
+static struct task_struct *preempt_task;
+static struct task_struct **reader_tasks;
+static struct task_struct *stats_task;
+
+#define HAZPTR_TORTURE_PIPE_LEN 10
+
+// Update-side data structure used to check RCU readers.
+struct hazptr_torture {
+ void *obj_hazptr;
+ int htort_pipe_count;
+ struct list_head htort_free;
+};
+
+static LIST_HEAD(hazptr_torture_freelist);
+static struct hazptr_torture /* __hazptr @@@ */ *hazptr_torture_current;
+static unsigned long hazptr_torture_current_version;
+static struct hazptr_torture hazptr_tortures[10 * HAZPTR_TORTURE_PIPE_LEN];
+static DEFINE_SPINLOCK(hazptr_torture_lock);
+static DEFINE_PER_CPU(long [HAZPTR_TORTURE_PIPE_LEN + 1], hazptr_torture_count);
+static atomic_t hazptr_torture_wcount[HAZPTR_TORTURE_PIPE_LEN + 1];
+static atomic_t n_hazptr_torture_alloc;
+static atomic_t n_hazptr_torture_alloc_fail;
+static atomic_t n_hazptr_torture_free;
+static atomic_t n_hazptr_torture_error;
+static struct list_head hazptr_torture_removed;
+
+/* @@@ */ static int hazptr_torture_writer_state;
+#define HTWS_FIXED_DELAY 0
+#define HTWS_DELAY 1
+#define HTWS_REPLACE 2
+#define HTWS_SYNC 3
+#define HTWS_STUTTER 4
+#define HTWS_STOPPING 5
+static const char * const hazptr_torture_writer_state_names[] = {
+ "HTWS_FIXED_DELAY",
+ "HTWS_DELAY",
+ "HTWS_REPLACE",
+ "HTWS_SYNC",
+ "HTWS_STUTTER",
+ "HTWS_STOPPING",
+};
+
+static const char *hazptr_torture_writer_state_getname(void)
+{
+ unsigned int i = READ_ONCE(hazptr_torture_writer_state);
+
+ if (i >= ARRAY_SIZE(hazptr_torture_writer_state_names))
+ return "???";
+ return hazptr_torture_writer_state_names[i];
+}
+
+/*
+ * Allocate an element from the hazptr_tortures pool.
+ */
+static struct hazptr_torture *hazptr_torture_alloc(void)
+{
+ struct list_head *p;
+
+ spin_lock_bh(&hazptr_torture_lock);
+ if (list_empty(&hazptr_torture_freelist)) {
+ atomic_inc(&n_hazptr_torture_alloc_fail);
+ spin_unlock_bh(&hazptr_torture_lock);
+ return NULL;
+ }
+ atomic_inc(&n_hazptr_torture_alloc);
+ p = hazptr_torture_freelist.next;
+ list_del_init(p);
+ spin_unlock_bh(&hazptr_torture_lock);
+ return container_of(p, struct hazptr_torture, htort_free);
+}
+
+/*
+ * Free an element to the hazptr_tortures pool.
+ */
+static void
+hazptr_torture_free(struct hazptr_torture *p)
+{
+ atomic_inc(&n_hazptr_torture_free);
+ spin_lock_bh(&hazptr_torture_lock);
+ list_add_tail(&p->htort_free, &hazptr_torture_freelist);
+ spin_unlock_bh(&hazptr_torture_lock);
+}
+
+/*
+ * Update object in the pipe. This should be invoked after a suitable time.
+ */
+static bool
+hazptr_torture_pipe_update_one(struct hazptr_torture *rp)
+{
+ int i;
+
+ i = rp->htort_pipe_count;
+ if (i > HAZPTR_TORTURE_PIPE_LEN)
+ i = HAZPTR_TORTURE_PIPE_LEN;
+ atomic_inc(&hazptr_torture_wcount[i]);
+ WRITE_ONCE(rp->htort_pipe_count, i + 1);
+ ASSERT_EXCLUSIVE_WRITER(rp->htort_pipe_count);
+ if (i + 1 >= HAZPTR_TORTURE_PIPE_LEN)
+ return true;
+ return false;
+}
+
+/*
+ * Update all callbacks in the pipe each time period.
+ */
+static void
+hazptr_torture_pipe_update(struct hazptr_torture *old_rp)
+{
+ struct hazptr_torture *rp;
+ struct hazptr_torture *rp1;
+
+ if (old_rp)
+ list_add(&old_rp->htort_free, &hazptr_torture_removed);
+ list_for_each_entry_safe(rp, rp1, &hazptr_torture_removed, htort_free) {
+ if (hazptr_torture_pipe_update_one(rp)) {
+ list_del(&rp->htort_free);
+ hazptr_torture_free(rp);
+ }
+ }
+}
+
+/*
+ * Operations vector for selecting different types of tests.
+ */
+
+struct hazptr_torture_ops {
+ void (*init)(void);
+ void (*cleanup)(void);
+ struct hazptr_torture *((*readlock)(struct hazptr_ctx **hcpp));
+ void (*read_delay)(struct torture_random_state *rrsp);
+ void (*readunlock)(struct hazptr_ctx *hcp, struct hazptr_torture *htp);
+ // @@@ int (*readlock_held)(void); // lockdep.
+ // @@@ int (*readlock_nesting)(void); // actual nesting, if available, -1 if not.
+ // @@@ void (*deferred_free)(struct rcu_torture *p); @@@ call_hazptr()
+ void (*sync)(void *htp);
+ // @@@ void (*stats)(void); If statistics must be extracted from hazptr.c.
+ int irq_capable;
+ int must_free_ctx;
+ const char *name;
+};
+
+static struct hazptr_torture_ops *cur_ops;
+
+/*
+ * Definitions for hazard-pointer torture testing.
+ */
+
+static struct hazptr_torture *hazptr_torture_read_lock(struct hazptr_ctx **hcpp)
+{
+ struct hazptr_ctx *hcp = kmalloc(sizeof(*hcp), GFP_KERNEL);
+
+ *hcpp = hcp;
+ if (!hcp)
+ return NULL;
+ return (struct hazptr_torture *)hazptr_acquire(hcp, (void *)&hazptr_torture_current);
+}
+
+static void hazptr_read_delay(struct torture_random_state *rrsp)
+{
+ const unsigned long shortdelay_us = 200;
+ unsigned long longdelay_ms = 300;
+
+ /* We want a short delay sometimes to make a reader delay the grace
+ * period, and we want a long delay occasionally to trigger
+ * force_quiescent_state. */
+
+ if (!(torture_random(rrsp) % (nrealreaders * 2000 * longdelay_ms))) {
+ if ((preempt_count() & HARDIRQ_MASK) || softirq_count())
+ longdelay_ms = 5; /* Avoid triggering BH limits. */
+ mdelay(longdelay_ms);
+ }
+ if (!(torture_random(rrsp) % (nrealreaders * 2 * shortdelay_us)))
+ udelay(shortdelay_us);
+ if (!preempt_count() && !(torture_random(rrsp) % (nrealreaders * 500)))
+ torture_preempt_schedule(); /* QS only if preemptible. */
+}
+
+static void hazptr_torture_read_unlock(struct hazptr_ctx *hcp, struct hazptr_torture *htp)
+{
+ if (hcp) {
+ hazptr_release(hcp, htp);
+ if (cur_ops->must_free_ctx)
+ kfree(hcp);
+ }
+}
+
+static void hazptr_sync_torture_init(void)
+{
+ INIT_LIST_HEAD(&hazptr_torture_removed);
+}
+
+static struct hazptr_torture_ops hazptr_ops = {
+ .init = hazptr_sync_torture_init,
+ .readlock = hazptr_torture_read_lock,
+ .read_delay = hazptr_read_delay,
+ .readunlock = hazptr_torture_read_unlock,
+ .sync = hazptr_synchronize,
+ .irq_capable = 1,
+ .must_free_ctx = 1,
+ .name = "hazptr"
+};
+
+/*
+ * Hazard-pointer torture writer kthread. Repeatedly substitutes a new
+ * structure for that pointed to by hazptr_torture_current, freeing the
+ * old structure after a series of timeouts (the "pipeline").
+ */
+static int
+hazptr_torture_writer(void *arg)
+{
+ bool booting_still = false;
+ int i;
+ unsigned long j;
+ int oldnice = task_nice(current);
+ struct hazptr_torture *rp;
+ struct hazptr_torture *old_rp;
+ static DEFINE_TORTURE_RANDOM(rand);
+ bool stutter_waited;
+
+ VERBOSE_TOROUT_STRING("hazptr_torture_writer task started");
+ // If the system is still booting, let it finish.
+ j = jiffies;
+ while (!torture_must_stop() && !rcu_inkernel_boot_has_ended()) {
+ booting_still = true;
+ schedule_timeout_interruptible(HZ);
+ }
+ if (booting_still)
+ pr_alert("%s" TORTURE_FLAG " Waited %lu jiffies for boot to complete.\n",
+ torture_type, jiffies - j);
+
+ do {
+ hazptr_torture_writer_state = HTWS_FIXED_DELAY;
+ torture_hrtimeout_us(500, 1000, &rand);
+ rp = hazptr_torture_alloc();
+ if (rp == NULL)
+ continue;
+ rp->htort_pipe_count = 0;
+ ASSERT_EXCLUSIVE_WRITER(rp->htort_pipe_count);
+ hazptr_torture_writer_state = HTWS_DELAY;
+ udelay(torture_random(&rand) & 0x3ff);
+ hazptr_torture_writer_state = HTWS_REPLACE;
+ old_rp = READ_ONCE(hazptr_torture_current);
+ smp_store_release(&hazptr_torture_current, rp);
+ smp_wmb(); /* Mods to old_rp must follow smp_store_release() */
+ if (old_rp) {
+ i = old_rp->htort_pipe_count;
+ if (i > HAZPTR_TORTURE_PIPE_LEN)
+ i = HAZPTR_TORTURE_PIPE_LEN;
+ atomic_inc(&hazptr_torture_wcount[i]);
+ WRITE_ONCE(old_rp->htort_pipe_count,
+ old_rp->htort_pipe_count + 1);
+ ASSERT_EXCLUSIVE_WRITER(old_rp->htort_pipe_count);
+
+ hazptr_torture_writer_state = HTWS_SYNC;
+ cur_ops->sync((void *)old_rp);
+ hazptr_torture_pipe_update(old_rp);
+ }
+
+ WRITE_ONCE(hazptr_torture_current_version, hazptr_torture_current_version + 1);
+ hazptr_torture_writer_state = HTWS_STUTTER;
+ stutter_waited = stutter_wait("hazptr_torture_writer");
+ if (stutter_waited && !torture_must_stop())
+ for (i = 0; i < ARRAY_SIZE(hazptr_tortures); i++)
+ if (list_empty(&hazptr_tortures[i].htort_free) &&
+ READ_ONCE(hazptr_torture_current) != &hazptr_tortures[i]) {
+ tracing_off();
+ WARN(1, "%s: htort_pipe_count: %d\n", __func__, hazptr_tortures[i].htort_pipe_count);
+ rcu_ftrace_dump(DUMP_ALL);
+ break;
+ }
+ if (stutter_waited)
+ sched_set_normal(current, oldnice);
+ } while (!torture_must_stop());
+ hazptr_torture_current = NULL; // Let stats task know that we are done.
+ hazptr_torture_writer_state = HTWS_STOPPING;
+ torture_kthread_stopping("hazptr_torture_writer");
+ return 0;
+}
+
+/*
+ * Hazard-pointer torture reader kthread. Repeatedly dereferences
+ * hazptr_torture_current, incrementing the corresponding element of the
+ * pipeline array. The counter in the element should never be greater
+ * than 1, otherwise, the hazard-pointer implementation is broken.
+ */
+static int hazptr_torture_reader(void *arg)
+{
+ struct hazptr_ctx *hcp;
+ struct hazptr_torture *htp;
+ unsigned long lastsleep = jiffies;
+ long myid = (long)arg;
+ int mynumonline = myid;
+ int pipe_count;
+ DEFINE_TORTURE_RANDOM(rand);
+
+ VERBOSE_TOROUT_STRING("hazptr_torture_reader task started");
+ set_user_nice(current, MAX_NICE);
+ do {
+ htp = cur_ops->readlock(&hcp);
+ if (!htp) {
+ schedule_timeout_interruptible(HZ / 10);
+ continue;
+ }
+ if (time_after(jiffies, lastsleep) && !torture_must_stop()) {
+ torture_hrtimeout_us(500, 1000, &rand);
+ lastsleep = jiffies + 10;
+ }
+ cur_ops->read_delay(&rand);
+ preempt_disable();
+ pipe_count = READ_ONCE(htp->htort_pipe_count);
+ if (pipe_count > HAZPTR_TORTURE_PIPE_LEN) {
+ // Should not happen in a correct RCU implementation,
+ // happens quite often for torture_type=busted.
+ pipe_count = HAZPTR_TORTURE_PIPE_LEN;
+ }
+ if (pipe_count > 1)
+ rcu_ftrace_dump(DUMP_ALL);
+ __this_cpu_inc(hazptr_torture_count[pipe_count]);
+ preempt_enable();
+ cur_ops->readunlock(hcp, htp);
+ while (!torture_must_stop() &&
+ (torture_num_online_cpus() < mynumonline || !rcu_inkernel_boot_has_ended()))
+ schedule_timeout_interruptible(HZ / 5);
+ stutter_wait("hazptr_torture_reader");
+ } while (!torture_must_stop());
+ torture_kthread_stopping("hazptr_torture_reader");
+ return 0;
+}
+
+/*
+ * Print torture statistics. Caller must ensure that there is only one
+ * call to this function at a given time!!! This is normally accomplished
+ * by relying on the module system to only have one copy of the module
+ * loaded, and then by giving the hazptr_torture_stats kthread full control
+ * (or the init/cleanup functions when hazptr_torture_stats thread is
+ * not running).
+ */
+static void
+hazptr_torture_stats_print(void)
+{
+ const char *cp = hazptr_torture_writer_state_getname();;
+ int cpu;
+ int i;
+ long pipesummary[HAZPTR_TORTURE_PIPE_LEN + 1] = { 0 };
+ long batchsummary[HAZPTR_TORTURE_PIPE_LEN + 1] = { 0 };
+ struct hazptr_torture *rtcp;
+ static unsigned long rtcv_snap = ULONG_MAX;
+ static bool splatted;
+ struct task_struct *wtp;
+
+ for_each_possible_cpu(cpu)
+ for (i = 0; i < HAZPTR_TORTURE_PIPE_LEN + 1; i++)
+ pipesummary[i] += READ_ONCE(per_cpu(hazptr_torture_count, cpu)[i]);
+ for (i = HAZPTR_TORTURE_PIPE_LEN; i >= 0; i--) {
+ if (pipesummary[i] != 0)
+ break;
+ } // The value of variable "i" is used later, so don't clobber it!
+
+ pr_alert("%s%s ", torture_type, TORTURE_FLAG);
+ rtcp = READ_ONCE(hazptr_torture_current);
+ pr_cont("rtc: %p %s: %lu %s tfle: %d rta: %d rtaf: %d rtf: %d ",
+ rtcp,
+ rtcp && !rcu_stall_is_suppressed_at_boot() ? "ver" : "VER",
+ hazptr_torture_current_version,
+ cp,
+ list_empty(&hazptr_torture_freelist),
+ atomic_read(&n_hazptr_torture_alloc),
+ atomic_read(&n_hazptr_torture_alloc_fail),
+ atomic_read(&n_hazptr_torture_free));
+ torture_onoff_stats();
+
+ pr_alert("%s%s ", torture_type, TORTURE_FLAG);
+ if (i > 1) {
+ pr_cont("%s", "!!! ");
+ atomic_inc(&n_hazptr_torture_error);
+ WARN_ON_ONCE(i > 1); // Too-short grace period
+ }
+ pr_cont("Reader Pipe: ");
+ for (i = 0; i < HAZPTR_TORTURE_PIPE_LEN + 1; i++)
+ pr_cont(" %ld", pipesummary[i]);
+ pr_cont("\n");
+
+ pr_alert("%s%s ", torture_type, TORTURE_FLAG);
+ pr_cont("Reader Batch: ");
+ for (i = 0; i < HAZPTR_TORTURE_PIPE_LEN + 1; i++)
+ pr_cont(" %ld", batchsummary[i]);
+ pr_cont("\n");
+
+ pr_alert("%s%s ", torture_type, TORTURE_FLAG);
+ pr_cont("Free-Block Circulation: ");
+ for (i = 0; i < HAZPTR_TORTURE_PIPE_LEN + 1; i++) {
+ pr_cont(" %d", atomic_read(&hazptr_torture_wcount[i]));
+ }
+ pr_cont("\n");
+
+ if (rtcv_snap == hazptr_torture_current_version &&
+ READ_ONCE(hazptr_torture_current) &&
+ rcu_inkernel_boot_has_ended()) {
+ int __maybe_unused flags = 0;
+ unsigned long __maybe_unused gp_seq = 0;
+
+ wtp = READ_ONCE(writer_task);
+ pr_alert("??? Writer stall state %s(%d) g%lu f%#x ->state %#x cpu %d\n",
+ hazptr_torture_writer_state_getname(),
+ hazptr_torture_writer_state, gp_seq, flags,
+ wtp == NULL ? ~0U : wtp->__state,
+ wtp == NULL ? -1 : (int)task_cpu(wtp));
+ if (!splatted && wtp) {
+ sched_show_task(wtp);
+ splatted = true;
+ }
+ rcu_ftrace_dump(DUMP_ALL);
+ }
+ rtcv_snap = hazptr_torture_current_version;
+}
+
+/*
+ * Periodically prints torture statistics, if periodic statistics printing
+ * was specified via the stat_interval module parameter.
+ */
+static int
+hazptr_torture_stats(void *arg)
+{
+ VERBOSE_TOROUT_STRING("hazptr_torture_stats task started");
+ do {
+ schedule_timeout_interruptible(stat_interval * HZ);
+ hazptr_torture_stats_print();
+ torture_shutdown_absorb("hazptr_torture_stats");
+ } while (!torture_must_stop());
+ torture_kthread_stopping("hazptr_torture_stats");
+ return 0;
+}
+
+static void
+hazptr_torture_print_module_parms(struct hazptr_torture_ops *cur_ops, const char *tag)
+{
+ pr_alert("%s" TORTURE_FLAG
+ "--- %s: nreaders=%d "
+ "stat_interval=%d verbose=%d "
+ "shuffle_interval=%d stutter=%d irqreader=%d "
+ "onoff_interval=%d onoff_holdoff=%d\n",
+ torture_type, tag, nrealreaders,
+ stat_interval, verbose,
+ shuffle_interval, stutter, irqreader,
+ onoff_interval, onoff_holdoff);
+}
+
+// Randomly preempt online CPUs.
+static int hazptr_torture_preempt(void *unused)
+{
+ int cpu = -1;
+ DEFINE_TORTURE_RANDOM(rand);
+
+ schedule_timeout_idle(onoff_holdoff * HZ);
+ do {
+ // Wait for preempt_interval ms with up to 100us fuzz.
+ torture_hrtimeout_ms(preempt_interval, 100, &rand);
+ // Select online CPU.
+ cpu = cpumask_next(cpu, cpu_online_mask);
+ if (cpu >= nr_cpu_ids)
+ cpu = cpumask_next(-1, cpu_online_mask);
+ WARN_ON_ONCE(cpu >= nr_cpu_ids);
+ // Move to that CPU, if can't do so, retry later.
+ if (torture_sched_setaffinity(current->pid, cpumask_of(cpu), false))
+ continue;
+ // Preempt at high-ish priority, then reset to normal.
+ sched_set_fifo(current);
+ torture_sched_setaffinity(current->pid, cpu_present_mask, true);
+ mdelay(preempt_duration);
+ sched_set_normal(current, 0);
+ stutter_wait("hazptr_torture_preempt");
+ } while (!torture_must_stop());
+ torture_kthread_stopping("hazptr_torture_preempt");
+ return 0;
+}
+
+static void
+hazptr_torture_cleanup(void)
+{
+ int i;
+
+ if (torture_cleanup_begin())
+ return;
+ if (!cur_ops) {
+ torture_cleanup_end();
+ return;
+ }
+
+ 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++)
+ torture_stop_kthread(hazptr_torture_reader,
+ reader_tasks[i]);
+ kfree(reader_tasks);
+ reader_tasks = NULL;
+ }
+
+ torture_stop_kthread(hazptr_torture_stats, stats_task);
+
+ /* Do torture-type-specific cleanup operations. */
+ if (cur_ops->cleanup != NULL)
+ cur_ops->cleanup();
+
+ hazptr_torture_stats_print(); /* -After- the stats thread is stopped! */
+ if (atomic_read(&n_hazptr_torture_error))
+ hazptr_torture_print_module_parms(cur_ops, "End of test: FAILURE");
+ else if (torture_onoff_failures())
+ hazptr_torture_print_module_parms(cur_ops, "End of test: HAZPTR_HOTPLUG");
+ else
+ hazptr_torture_print_module_parms(cur_ops, "End of test: SUCCESS");
+ torture_cleanup_end();
+}
+
+static int __init hazptr_torture_init(void)
+{
+ long i;
+ int cpu;
+ int firsterr = 0;
+ static struct hazptr_torture_ops *torture_ops[] = { &hazptr_ops, };
+
+ if (!torture_init_begin(torture_type, verbose))
+ return -EBUSY;
+
+ /* Process args and tell the world that the torturer is on the job. */
+ for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
+ cur_ops = torture_ops[i];
+ if (strcmp(torture_type, cur_ops->name) == 0)
+ break;
+ }
+ if (i == ARRAY_SIZE(torture_ops)) {
+ pr_alert("hazptr-torture: invalid torture type: \"%s\"\n", torture_type);
+ pr_alert("hazptr-torture types:");
+ for (i = 0; i < ARRAY_SIZE(torture_ops); i++)
+ pr_cont(" %s", torture_ops[i]->name);
+ pr_cont("\n");
+ firsterr = -EINVAL;
+ cur_ops = NULL;
+ goto unwind;
+ }
+
+ if (cur_ops->init)
+ cur_ops->init();
+
+ if (nreaders >= 0) {
+ nrealreaders = nreaders;
+ } else {
+ nrealreaders = num_online_cpus() - 2 - nreaders;
+ if (nrealreaders <= 0)
+ nrealreaders = 1;
+ }
+ hazptr_torture_print_module_parms(cur_ops, "Start of test");
+
+ /* Set up the freelist. */
+ INIT_LIST_HEAD(&hazptr_torture_freelist);
+ for (i = 0; i < ARRAY_SIZE(hazptr_tortures); i++)
+ list_add_tail(&hazptr_tortures[i].htort_free, &hazptr_torture_freelist);
+
+ /* Initialize the statistics so that each run gets its own numbers. */
+
+ hazptr_torture_current = NULL;
+ hazptr_torture_current_version = 0;
+ atomic_set(&n_hazptr_torture_alloc, 0);
+ atomic_set(&n_hazptr_torture_alloc_fail, 0);
+ atomic_set(&n_hazptr_torture_free, 0);
+ atomic_set(&n_hazptr_torture_error, 0);
+ for (i = 0; i < HAZPTR_TORTURE_PIPE_LEN + 1; i++)
+ atomic_set(&hazptr_torture_wcount[i], 0);
+ for_each_possible_cpu(cpu) {
+ for (i = 0; i < HAZPTR_TORTURE_PIPE_LEN + 1; i++)
+ per_cpu(hazptr_torture_count, cpu)[i] = 0;
+ }
+
+ /* Start up the kthreads. */
+
+ reader_tasks = kzalloc_objs(reader_tasks[0], nrealreaders);
+ for (i = 0; i < nrealreaders; i++) {
+ firsterr = torture_create_kthread(hazptr_torture_reader, (void *)i,
+ reader_tasks[i]);
+ if (torture_init_error(firsterr))
+ goto unwind;
+ }
+
+ firsterr = torture_create_kthread(hazptr_torture_writer, NULL, writer_task);
+ if (torture_init_error(firsterr))
+ goto unwind;
+
+ if (stat_interval > 0) {
+ firsterr = torture_create_kthread(hazptr_torture_stats, NULL, stats_task);
+ if (torture_init_error(firsterr))
+ goto unwind;
+ }
+ if (shuffle_interval > 0) {
+ firsterr = torture_shuffle_init(shuffle_interval * HZ);
+ if (torture_init_error(firsterr))
+ goto unwind;
+ }
+ if (stutter < 0)
+ stutter = 0;
+ if (stutter) {
+ int t;
+
+ t = stutter * HZ;
+ firsterr = torture_stutter_init(stutter * HZ, t);
+ if (torture_init_error(firsterr))
+ goto unwind;
+ }
+ firsterr = torture_shutdown_init(shutdown_secs, hazptr_torture_cleanup);
+ if (torture_init_error(firsterr))
+ goto unwind;
+ if (preempt_duration > 0) {
+ firsterr = torture_create_kthread(hazptr_torture_preempt, NULL, preempt_task);
+ if (torture_init_error(firsterr))
+ goto unwind;
+ }
+
+ torture_init_end();
+ return 0;
+
+unwind:
+ torture_init_end();
+ hazptr_torture_cleanup();
+ if (shutdown_secs) {
+ WARN_ON(!IS_MODULE(CONFIG_HAZPTR_TORTURE_TEST));
+ kernel_power_off();
+ }
+ return firsterr;
+}
+
+module_init(hazptr_torture_init);
+module_exit(hazptr_torture_cleanup);
diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c
index b62735a6788423..2a778b8ab4ad78 100644
--- a/kernel/rcu/update.c
+++ b/kernel/rcu/update.c
@@ -44,6 +44,7 @@
#include <linux/slab.h>
#include <linux/irq_work.h>
#include <linux/rcupdate_trace.h>
+#include <linux/torture.h>
#define CREATE_TRACE_POINTS
@@ -525,7 +526,7 @@ EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read);
do { } while (0)
#endif
-#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST) || IS_ENABLED(CONFIG_LOCK_TORTURE_TEST) || IS_MODULE(CONFIG_LOCK_TORTURE_TEST)
+#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_ENABLED(CONFIG_LOCK_TORTURE_TEST) || IS_ENABLED(CONFIG_HAZPTR_TORTURE_TEST)
/* Get rcutorture access to sched_setaffinity(). */
long torture_sched_setaffinity(pid_t pid, const struct cpumask *in_mask, bool dowarn)
{
diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh
index dfb73a3461a6df..14570f5e3ce17b 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm.sh
@@ -91,7 +91,7 @@ usage () {
echo " --remote"
echo " --results absolute-pathname"
echo " --shutdown-grace seconds"
- echo " --torture lock|rcu|rcuscale|refscale|repro|scf|X*"
+ echo " --torture hazptr|lock|rcu|rcuscale|refscale|repro|scf|X*"
echo " --trust-make"
exit 1
}
@@ -256,9 +256,9 @@ do
shift
;;
--torture)
- checkarg --torture "(suite name)" "$#" "$2" '^\(lock\|rcu\|rcuscale\|refscale\|repro\|scf\|X.*\)$' '^--'
+ checkarg --torture "(suite name)" "$#" "$2" '^\(hazptr\|lock\|rcu\|rcuscale\|refscale\|repro\|scf\|X.*\)$' '^--'
TORTURE_SUITE=$2
- TORTURE_MOD="`echo $TORTURE_SUITE | sed -e 's/^\(lock\|rcu\|scf\)$/\1torture/'`"
+ TORTURE_MOD="`echo $TORTURE_SUITE | sed -e 's/^\(hazptr\|lock\|rcu\|scf\)$/\1torture/'`"
shift
if test "$TORTURE_SUITE" = rcuscale || test "$TORTURE_SUITE" = refscale
then
diff --git a/tools/testing/selftests/rcutorture/configs/hazptr/CFLIST b/tools/testing/selftests/rcutorture/configs/hazptr/CFLIST
new file mode 100644
index 00000000000000..4d62eb4a39f999
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/hazptr/CFLIST
@@ -0,0 +1,2 @@
+NOPREEMPT
+PREEMPT
diff --git a/tools/testing/selftests/rcutorture/configs/hazptr/CFcommon b/tools/testing/selftests/rcutorture/configs/hazptr/CFcommon
new file mode 100644
index 00000000000000..c440d227007dce
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/hazptr/CFcommon
@@ -0,0 +1,2 @@
+CONFIG_HAZPTR_TORTURE_TEST=y
+CONFIG_PRINTK_TIME=y
diff --git a/tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT b/tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT
new file mode 100644
index 00000000000000..e2da430abe4d70
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT
@@ -0,0 +1,17 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=16
+CONFIG_PREEMPT_LAZY=y
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=n
+CONFIG_PREEMPT_DYNAMIC=n
+CONFIG_HZ_PERIODIC=n
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NO_HZ_FULL=n
+CONFIG_HOTPLUG_CPU=y
+CONFIG_SUSPEND=n
+CONFIG_HIBERNATION=n
+CONFIG_DEBUG_LOCK_ALLOC=n
+CONFIG_PROVE_LOCKING=n
+CONFIG_KPROBES=n
+CONFIG_FTRACE=n
diff --git a/tools/testing/selftests/rcutorture/configs/hazptr/PREEMPT b/tools/testing/selftests/rcutorture/configs/hazptr/PREEMPT
new file mode 100644
index 00000000000000..b8ea4364b20b7b
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/hazptr/PREEMPT
@@ -0,0 +1,14 @@
+CONFIG_SMP=y
+CONFIG_NR_CPUS=16
+CONFIG_PREEMPT_NONE=n
+CONFIG_PREEMPT_VOLUNTARY=n
+CONFIG_PREEMPT=y
+CONFIG_HZ_PERIODIC=n
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NO_HZ_FULL=n
+CONFIG_HOTPLUG_CPU=y
+CONFIG_SUSPEND=n
+CONFIG_HIBERNATION=n
+CONFIG_DEBUG_LOCK_ALLOC=n
+CONFIG_PROVE_LOCKING=n
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/configs/hazptr/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/hazptr/ver_functions.sh
new file mode 100644
index 00000000000000..a28ea2f292e453
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/hazptr/ver_functions.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Kernel-version-dependent shell functions for the rest of the scripts.
+#
+# Claude created this file, and I quote:
+#
+# "I created [this file] modeled on the lock torture
+# version. It defines per_version_boot_params to pass
+# hazptrtorture.shutdown_secs=$3, hazptrtorture.stat_interval=15,
+# hazptrtorture.verbose=1, and optional CPU-hotplug parameters to
+# the kernel command line."
+#
+# I therefore kept locktorture's ver_functions.sh copyright notice:
+#
+# Copyright (C) Meta Platforms, Inc. and affiliates.
+#
+# Authors: Paul E. McKenney <paulmck@kernel.org>
+
+# hazptrtorture_param_onoff bootparam-string config-file
+#
+# Adds onoff hazptrtorture module parameters to kernels having it.
+hazptrtorture_param_onoff () {
+ if ! bootparam_hotplug_cpu "$1" && configfrag_hotplug_cpu "$2"
+ then
+ echo CPU-hotplug kernel, adding hazptrtorture onoff. 1>&2
+ echo hazptrtorture.onoff_interval=3 hazptrtorture.onoff_holdoff=30
+ fi
+}
+
+# per_version_boot_params bootparam-string config-file seconds
+#
+# Adds per-version torture-module parameters to kernels supporting them.
+per_version_boot_params () {
+ echo `hazptrtorture_param_onoff "$1" "$2"` \
+ hazptrtorture.stat_interval=15 \
+ hazptrtorture.shutdown_secs=$3 \
+ hazptrtorture.verbose=1 \
+ $1
+}
--
2.40.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH RFC 2/4] hazptrtorture: Add testing of on-stack hazptr_ctx structures
2026-05-07 16:50 [PATCH RFC 0/4] Hazard-pointer torture test Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 1/4] torture: Add a hazptrtorture.c " Paul E. McKenney
@ 2026-05-07 16:51 ` Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 3/4] hazptrtorture: Add microsecond-scale sleep in readers Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 4/4] hazptrtorture: Enable system-independent CPU overcommit Paul E. McKenney
3 siblings, 0 replies; 5+ messages in thread
From: Paul E. McKenney @ 2026-05-07 16:51 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Mathieu Desnoyers,
Paul E. McKenney
This commit adds a test using on-stack hazptr_ctx structures, in contrast
with the per-CPU structures used by the initial test.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
kernel/rcu/hazptrtorture.c | 48 +++++++++++++++----
.../rcutorture/configs/hazptr/NOPREEMPT.boot | 1 +
2 files changed, 39 insertions(+), 10 deletions(-)
create mode 100644 tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT.boot
diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
index 1949a8da4f8c9d..1a61d35ade1d9d 100644
--- a/kernel/rcu/hazptrtorture.c
+++ b/kernel/rcu/hazptrtorture.c
@@ -30,9 +30,10 @@ MODULE_DESCRIPTION("Hazard-pointer module-based torture test facility");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Paul E. McKenney <paulmckrcu@meta.com>");
-torture_param(int, irqreader, 1, "Allow hazard-pointer readers from irq handlers");
+// @@@ torture_param(int, irqreader, 1, "Allow hazard-pointer readers from irq handlers");
// @@@ torture_param(int, leakpointer, 0, "Leak pointer dereferences from readers");
torture_param(int, nreaders, -1, "Number of hazard-pointer reader threads");
+// @@@ Does testing CPU hotplug make sense for hazard pointers?
torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)");
torture_param(int, onoff_interval, 0, "Time between CPU hotplugs (jiffies), 0=disable");
// @@@ Move the rcu_torture_preempt() function and friends to kernel/torture.c.
@@ -194,7 +195,8 @@ struct hazptr_torture_ops {
static struct hazptr_torture_ops *cur_ops;
/*
- * Definitions for hazard-pointer torture testing.
+ * Definitions for hazard-pointer torture testing using per-CPU hazptr_ctx
+ * structures.
*/
static struct hazptr_torture *hazptr_torture_read_lock(struct hazptr_ctx **hcpp)
@@ -252,6 +254,29 @@ static struct hazptr_torture_ops hazptr_ops = {
.name = "hazptr"
};
+/*
+ * Definitions for hazard-pointer torture testing using on-stack
+ * hazptr_ctx structures.
+ */
+
+static struct hazptr_torture *hazptr_torture_read_lock_stack(struct hazptr_ctx **hcpp)
+{
+ struct hazptr_torture *htp;
+
+ htp = (struct hazptr_torture *)hazptr_acquire(*hcpp, (void *)&hazptr_torture_current);
+ return htp;
+}
+
+static struct hazptr_torture_ops hazptr_stack_ops = {
+ .init = hazptr_sync_torture_init,
+ .readlock = hazptr_torture_read_lock_stack,
+ .read_delay = hazptr_read_delay,
+ .readunlock = hazptr_torture_read_unlock,
+ .sync = hazptr_synchronize,
+ .irq_capable = 1,
+ .name = "hazptr-stack"
+};
+
/*
* Hazard-pointer torture writer kthread. Repeatedly substitutes a new
* structure for that pointed to by hazptr_torture_current, freeing the
@@ -337,7 +362,8 @@ hazptr_torture_writer(void *arg)
*/
static int hazptr_torture_reader(void *arg)
{
- struct hazptr_ctx *hcp;
+ struct hazptr_ctx hc;
+ struct hazptr_ctx *hcp = &hc;
struct hazptr_torture *htp;
unsigned long lastsleep = jiffies;
long myid = (long)arg;
@@ -488,13 +514,15 @@ hazptr_torture_print_module_parms(struct hazptr_torture_ops *cur_ops, const char
{
pr_alert("%s" TORTURE_FLAG
"--- %s: nreaders=%d "
- "stat_interval=%d verbose=%d "
- "shuffle_interval=%d stutter=%d irqreader=%d "
- "onoff_interval=%d onoff_holdoff=%d\n",
+ "onoff_interval=%d onoff_holdoff=%d "
+ "preempt_duration=%d preempt_interval=%d "
+ "shuffle_interval=%d shutdown_secs=%d stat_interval=%d stutter=%d "
+ "verbose=%d\n",
torture_type, tag, nrealreaders,
- stat_interval, verbose,
- shuffle_interval, stutter, irqreader,
- onoff_interval, onoff_holdoff);
+ onoff_interval, onoff_holdoff,
+ preempt_duration, preempt_interval,
+ shuffle_interval, shutdown_secs, stat_interval, stutter,
+ verbose);
}
// Randomly preempt online CPUs.
@@ -570,7 +598,7 @@ static int __init hazptr_torture_init(void)
long i;
int cpu;
int firsterr = 0;
- static struct hazptr_torture_ops *torture_ops[] = { &hazptr_ops, };
+ static struct hazptr_torture_ops *torture_ops[] = { &hazptr_ops, &hazptr_stack_ops, };
if (!torture_init_begin(torture_type, verbose))
return -EBUSY;
diff --git a/tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT.boot b/tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT.boot
new file mode 100644
index 00000000000000..1d09a6446080a1
--- /dev/null
+++ b/tools/testing/selftests/rcutorture/configs/hazptr/NOPREEMPT.boot
@@ -0,0 +1 @@
+hazptrtorture.torture_type=hazptr-stack
--
2.40.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH RFC 3/4] hazptrtorture: Add microsecond-scale sleep in readers
2026-05-07 16:50 [PATCH RFC 0/4] Hazard-pointer torture test Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 1/4] torture: Add a hazptrtorture.c " Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 2/4] hazptrtorture: Add testing of on-stack hazptr_ctx structures Paul E. McKenney
@ 2026-05-07 16:51 ` Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 4/4] hazptrtorture: Enable system-independent CPU overcommit Paul E. McKenney
3 siblings, 0 replies; 5+ messages in thread
From: Paul E. McKenney @ 2026-05-07 16:51 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Mathieu Desnoyers,
Paul E. McKenney
This commit adds a default-disabled reader_sleep_us module parameter
that causes the hazard-pointer reader to unconditionally sleep for the
specified number of microseconds.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
kernel/rcu/hazptrtorture.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
index 1a61d35ade1d9d..219fd90a7f8840 100644
--- a/kernel/rcu/hazptrtorture.c
+++ b/kernel/rcu/hazptrtorture.c
@@ -39,6 +39,7 @@ torture_param(int, onoff_interval, 0, "Time between CPU hotplugs (jiffies), 0=di
// @@@ Move the rcu_torture_preempt() function and friends to kernel/torture.c.
torture_param(int, preempt_duration, 0, "Preemption duration (ms), zero to disable");
torture_param(int, preempt_interval, MSEC_PER_SEC, "Interval between preemptions (ms)");
+torture_param(int, reader_sleep_us, 0, "Reader sleep duration (us)");
torture_param(int, shuffle_interval, 3, "Number of seconds between shuffles");
torture_param(int, shutdown_secs, 0, "Shutdown time (s), <= zero to disable.");
torture_param(int, stat_interval, 60, "Number of seconds between stats printk()s");
@@ -227,6 +228,8 @@ static void hazptr_read_delay(struct torture_random_state *rrsp)
udelay(shortdelay_us);
if (!preempt_count() && !(torture_random(rrsp) % (nrealreaders * 500)))
torture_preempt_schedule(); /* QS only if preemptible. */
+ if (reader_sleep_us > 0)
+ torture_hrtimeout_us(reader_sleep_us, 0, NULL);
}
static void hazptr_torture_read_unlock(struct hazptr_ctx *hcp, struct hazptr_torture *htp)
@@ -516,11 +519,13 @@ hazptr_torture_print_module_parms(struct hazptr_torture_ops *cur_ops, const char
"--- %s: nreaders=%d "
"onoff_interval=%d onoff_holdoff=%d "
"preempt_duration=%d preempt_interval=%d "
+ "reader_sleep_us=%d "
"shuffle_interval=%d shutdown_secs=%d stat_interval=%d stutter=%d "
"verbose=%d\n",
torture_type, tag, nrealreaders,
onoff_interval, onoff_holdoff,
preempt_duration, preempt_interval,
+ reader_sleep_us,
shuffle_interval, shutdown_secs, stat_interval, stutter,
verbose);
}
--
2.40.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH RFC 4/4] hazptrtorture: Enable system-independent CPU overcommit
2026-05-07 16:50 [PATCH RFC 0/4] Hazard-pointer torture test Paul E. McKenney
` (2 preceding siblings ...)
2026-05-07 16:51 ` [PATCH RFC 3/4] hazptrtorture: Add microsecond-scale sleep in readers Paul E. McKenney
@ 2026-05-07 16:51 ` Paul E. McKenney
3 siblings, 0 replies; 5+ messages in thread
From: Paul E. McKenney @ 2026-05-07 16:51 UTC (permalink / raw)
To: rcu; +Cc: linux-kernel, kernel-team, rostedt, Mathieu Desnoyers,
Paul E. McKenney
This commit interprets negative values of the nreaders module parameter
as a number of readers per CPU, so that hazptrtorture.nreaders=-5 would
spawn five hazard-pointer reader kthreads per CPU.
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
---
kernel/rcu/hazptrtorture.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/rcu/hazptrtorture.c b/kernel/rcu/hazptrtorture.c
index 219fd90a7f8840..c266d0b64dfde1 100644
--- a/kernel/rcu/hazptrtorture.c
+++ b/kernel/rcu/hazptrtorture.c
@@ -631,7 +631,7 @@ static int __init hazptr_torture_init(void)
if (nreaders >= 0) {
nrealreaders = nreaders;
} else {
- nrealreaders = num_online_cpus() - 2 - nreaders;
+ nrealreaders = num_online_cpus() * -nreaders;
if (nrealreaders <= 0)
nrealreaders = 1;
}
--
2.40.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-07 16:51 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-07 16:50 [PATCH RFC 0/4] Hazard-pointer torture test Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 1/4] torture: Add a hazptrtorture.c " Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 2/4] hazptrtorture: Add testing of on-stack hazptr_ctx structures Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 3/4] hazptrtorture: Add microsecond-scale sleep in readers Paul E. McKenney
2026-05-07 16:51 ` [PATCH RFC 4/4] hazptrtorture: Enable system-independent CPU overcommit Paul E. McKenney
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox