// 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 */ #define pr_fmt(fmt) fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rcu.h" MODULE_DESCRIPTION("Hazard-pointer module-based torture test facility"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Paul E. McKenney "); torture_param(int, defer_modulus, -1, "Defer once per specified # of hazptr ops, zero to disable"); torture_param(int, irq_acquire, -1, "Acquire hazard pointers from irq handlers once per specified #, zero to disable"); 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, 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)"); torture_param(int, onoff_interval, 0, "Time between CPU hotplugs (jiffies), 0=disable"); 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"); 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 *do_pending_task; 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_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 DEFINE_PER_CPU(atomic_long_t, hazptr_torture_acquires); static DEFINE_PER_CPU(atomic_long_t, hazptr_torture_releases); static DEFINE_PER_CPU(atomic_long_t, hazptr_torture_acquires_irq); static DEFINE_PER_CPU(atomic_long_t, hazptr_torture_releases_irq); static DEFINE_PER_CPU(atomic_long_t, hazptr_torture_releases_defer); static DEFINE_PER_CPU(atomic_long_t, hazptr_torture_releases_undefer); static struct list_head hazptr_torture_removed; // State for a deferred (AKA pending) hazard pointer struct hazptr_pending { struct llist_node hpp_node; struct hazptr_ctx hpp_hc; struct hazptr_torture *hpp_htp; }; static DEFINE_PER_CPU(struct llist_head, hazptr_pending); 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); void (*sync)(void *htp); int irq_capable; int onstack_ctx; const char *name; }; static struct hazptr_torture_ops *cur_ops; /* * Definitions for hazard-pointer torture testing using per-CPU hazptr_ctx * structures. */ static struct hazptr_torture *hazptr_torture_read_lock(struct hazptr_ctx *hcpp) { struct hazptr_torture *htp; htp = (struct hazptr_torture *)hazptr_acquire(hcpp, (void *)&hazptr_torture_current); return htp; } static void hazptr_read_delay(struct torture_random_state *rrsp) { const bool can_sleep = !preempt_count() && !irqs_disabled(); const unsigned long shortdelay_us = 200; unsigned long longdelay_ms = 300; const bool short_spin = irqs_disabled() || irq_count(); // 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 (short_spin) longdelay_ms = 5; /* Avoid triggering BH limits. */ mdelay(longdelay_ms); } if (!(torture_random(rrsp) % (nrealreaders * 2 * shortdelay_us))) udelay(shortdelay_us); if (can_sleep && !(torture_random(rrsp) % (nrealreaders * 500))) torture_preempt_schedule(); /* QS only if preemptible. */ if (can_sleep && 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) { if (hcp) { hazptr_release(hcp, htp); } } 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, .name = "hazptr" }; /* * Definitions for hazard-pointer torture testing using on-stack * hazptr_ctx structures. */ static struct hazptr_torture_ops hazptr_stack_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, .onstack_ctx = 1, .name = "hazptr-stack" }; /* * 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); // Publish new structure. 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; } /* * Acquire a hazard pointer from an smp_call_function handler. */ static void hazptr_torture_acquire(void *hppp_in) { struct hazptr_pending *hppp = hppp_in; hppp->hpp_htp = cur_ops->readlock(&hppp->hpp_hc); /* * Acquiring a hazard pointer from a remote CPU. * Detach hazptr from its task so it can be released by another task. */ hazptr_detach(&hppp->hpp_hc); atomic_long_inc(per_cpu_ptr(&hazptr_torture_acquires_irq, raw_smp_processor_id())); } /* * Release a hazard pointer from an smp_call_function handler. */ static void hazptr_torture_release(void *hppp_in) { struct hazptr_pending *hppp = hppp_in; cur_ops->readunlock(&hppp->hpp_hc, hppp->hpp_htp); atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_irq, raw_smp_processor_id())); } /* * Do the delay, the accounting, and the release. This in intended to * be invoked from hazptr_torture_reader, but also for hazard pointers * sent off to interrupt handlers and the like. */ static void hazptr_torture_reader_tail(struct hazptr_pending *hppp, struct torture_random_state *trsp) { int cpu; struct hazptr_ctx *hcp = &hppp->hpp_hc; struct hazptr_torture *htp = hppp->hpp_htp; int pipe_count; cur_ops->read_delay(trsp); preempt_disable(); pipe_count = READ_ONCE(htp->htort_pipe_count); if (pipe_count > HAZPTR_TORTURE_PIPE_LEN) { // Should not happen in a correct hazptr implementation, // happens quite often for TBD 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(); if (irq_release && !(torture_random(trsp) % irq_release)) { guard(preempt)(); /* * Handling release from a remote CPU. * Detach hazptr from its task so it can be released by another task. */ hazptr_detach(&hppp->hpp_hc); cpu = cpumask_next_wrap(smp_processor_id(), cpu_online_mask); smp_call_function_single(cpu, hazptr_torture_release, hppp, 1); } else { cur_ops->readunlock(hcp, htp); atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases, raw_smp_processor_id())); } } /* * Defer the specified hazard pointer to some other context. */ static void hazptr_torture_defer(struct hazptr_pending *hppp, struct torture_random_state *trsp) { int cpu = torture_random(trsp) % nr_cpu_ids; struct llist_head *llhp; guard(preempt)(); /* * Handling release from a remote CPU. * Detach hazptr from its task so it can be released by another task. */ hazptr_detach(&hppp->hpp_hc); cpu = cpumask_next_wrap(cpu, cpu_online_mask); llhp = per_cpu_ptr(&hazptr_pending, cpu); llist_add(&hppp->hpp_node, llhp); atomic_long_inc(per_cpu_ptr(&hazptr_torture_releases_defer, raw_smp_processor_id())); } /* * 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) { bool can_defer = !cur_ops->onstack_ctx && kthread_do_pending_ms && defer_modulus; int cpu = 0; struct hazptr_pending hpp; struct hazptr_pending *hppp = cur_ops->onstack_ctx ? &hpp : NULL; unsigned long lastsleep = jiffies; long myid = (long)arg; int mynumonline = myid % nr_cpu_ids; DEFINE_TORTURE_RANDOM(rand); VERBOSE_TOROUT_STRING("hazptr_torture_reader task started"); set_user_nice(current, MAX_NICE); do { if (!hppp) { hppp = kmalloc_obj(*hppp, GFP_KERNEL); if (!hppp) { // Allocation failure, so get out of the way. schedule_timeout_interruptible(HZ / 10); continue; } } if (irq_acquire && !(torture_random(&rand) % irq_acquire)) { guard(preempt)(); cpu = cpumask_next_wrap(cpu, cpu_online_mask); if (cpu != smp_processor_id()) { smp_call_function_single(cpu, hazptr_torture_acquire, hppp, 1); } else { hppp->hpp_htp = cur_ops->readlock(&hppp->hpp_hc); atomic_long_inc(per_cpu_ptr(&hazptr_torture_acquires, raw_smp_processor_id())); } } else { hppp->hpp_htp = cur_ops->readlock(&hppp->hpp_hc); atomic_long_inc(per_cpu_ptr(&hazptr_torture_acquires, raw_smp_processor_id())); } if (!hppp->hpp_htp) { // Still starting up, so get out of the way. schedule_timeout_interruptible(HZ / 10); continue; } if (time_after(jiffies, lastsleep) && !torture_must_stop()) { torture_hrtimeout_us(500, 1000, &rand); lastsleep = jiffies + 10; } if (can_defer && !(torture_random(&rand) % defer_modulus)) { hazptr_torture_defer(hppp, &rand); hppp = NULL; } else { hazptr_torture_reader_tail(hppp, &rand); } 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; } /* * 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. */ static int hazptr_torture_do_pending(void *arg) { int cpu = 0; DEFINE_TORTURE_RANDOM(rand); VERBOSE_TOROUT_STRING("hazptr_torture_do_pending task started"); do { if (stutter_will_wait()) { for_each_possible_cpu(cpu) hazptr_torture_do_one_pending(cpu, &rand); } else { cpu = cpumask_next_wrap(cpu, cpu_possible_mask); hazptr_torture_do_one_pending(cpu, &rand); } if (!torture_must_stop()) torture_hrtimeout_ms(kthread_do_pending_ms, USEC_PER_MSEC, &rand); // Omit stutter_wait() because this function needs to do cleanup. } while (!torture_must_stop()); torture_kthread_stopping("hazptr_torture_do_pending"); return 0; } /* * Spawn hazptr_torture_do_pending() if there is something for it to do. */ static int hazptr_torture_do_pending_init(void) { if (kthread_do_pending_ms == -1) kthread_do_pending_ms = (cur_ops->onstack_ctx || defer_modulus == 0) ? 0 : 3; if (defer_modulus == -1) defer_modulus = (cur_ops->onstack_ctx || kthread_do_pending_ms == 0) ? 0 : 1000 * nr_cpu_ids; if (kthread_do_pending_ms < 0) { pr_alert("Cannot have negative kthread_do_pending_ms, disabling deferral.\n"); goto err_out; } if (cur_ops->onstack_ctx && kthread_do_pending_ms) { pr_alert("Cannot defer onstack hazptr_ctx, disabling deferral.\n"); goto err_out; } if (defer_modulus < 0) { pr_alert("Cannot have negative defer_modulus (%d), disabling deferral.\n", defer_modulus); goto err_out; } if (!kthread_do_pending_ms != !defer_modulus) { pr_alert("Pending kthread (%d) & deferral (%d) don't match, disabling deferral.\n", kthread_do_pending_ms, defer_modulus); goto err_out; } if (!kthread_do_pending_ms) return 0; return torture_create_kthread(hazptr_torture_do_pending, NULL, do_pending_task); err_out: WARN_ON(IS_BUILTIN(CONFIG_HAZPTR_TORTURE_TEST)); kthread_do_pending_ms = 0; defer_modulus = 0; 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_cont("acq: %lld rel: %lld acqirq: %lld relirq: %lld reldefer: %lld relundefer %lld\n", torture_sum_pcpu_atomic_long(&hazptr_torture_acquires), torture_sum_pcpu_atomic_long(&hazptr_torture_releases), torture_sum_pcpu_atomic_long(&hazptr_torture_acquires_irq), torture_sum_pcpu_atomic_long(&hazptr_torture_releases_irq), torture_sum_pcpu_atomic_long(&hazptr_torture_releases_defer), torture_sum_pcpu_atomic_long(&hazptr_torture_releases_undefer)); 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 %c cpu %d\n", hazptr_torture_writer_state_getname(), hazptr_torture_writer_state, gp_seq, flags, wtp == NULL ? '?' : task_state_to_char(wtp), 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 nwriters=%d " "defer_modulus=%d irq_acquire=%d irq_release=%d kthread_do_pending_ms=%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, nwriters, defer_modulus, irq_acquire, irq_release, kthread_do_pending_ms, onoff_interval, onoff_holdoff, preempt_duration, preempt_interval, reader_sleep_us, shuffle_interval, shutdown_secs, stat_interval, stutter, verbose); } // 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_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++) 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, &hazptr_stack_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() * -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. */ // This must be before the readers in order to set up the module // parameters used by the readers. firsterr = hazptr_torture_do_pending_init(); if (torture_init_error(firsterr)) goto unwind; if (irq_acquire == -1) { irq_acquire = 1000 * nr_cpu_ids; } else if (irq_acquire < 0) { pr_alert("Cannot have irq_acquire (%d) < -1, disabling.\n", irq_acquire); WARN_ON(IS_BUILTIN(CONFIG_HAZPTR_TORTURE_TEST)); irq_acquire = 0; } if (irq_release == -1) { irq_release = 1000 * nr_cpu_ids; } else if (irq_release < 0) { pr_alert("Cannot have irq_release (%d) < -1, disabling.\n", irq_release); WARN_ON(IS_BUILTIN(CONFIG_HAZPTR_TORTURE_TEST)); irq_release = 0; } 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; } if (nwriters) { firsterr = torture_create_kthread(hazptr_torture_writer, NULL, writer_task); if (torture_init_error(firsterr)) goto unwind; } firsterr = torture_onoff_init(onoff_holdoff * HZ, onoff_interval, NULL); 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);