summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-09 23:05:34 +0200
committerMark Brown <broonie@kernel.org>2026-10-09 23:05:34 +0200
commit629f361b5f967b46d06ba165efa7ec4d171ee45f (patch)
tree704831a6bdf57a0cb50c711fa2d802c7fe879b51 /kernel
parent712a092c09756a97f3188199ad33117c482220b4 (diff)
parent220752c6d02ab97ea019227c8296b7b137e009c7 (diff)
downloadlinux-next-629f361b5f967b46d06ba165efa7ec4d171ee45f.tar.gz
linux-next-629f361b5f967b46d06ba165efa7ec4d171ee45f.zip
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux
Diffstat (limited to 'kernel')
-rw-r--r--kernel/rcu/Kconfig6
-rw-r--r--kernel/rcu/rcu.h14
-rw-r--r--kernel/rcu/rcutorture.c317
-rw-r--r--kernel/rcu/srcutiny.c189
-rw-r--r--kernel/rcu/srcutree.c502
-rw-r--r--kernel/rcu/tasks.h15
-rw-r--r--kernel/rcu/tiny.c127
-rw-r--r--kernel/rcu/tree.c164
-rw-r--r--kernel/rcu/tree.h6
-rw-r--r--kernel/rcu/tree_exp.h2
-rw-r--r--kernel/rcu/tree_nocb.h3
-rw-r--r--kernel/rcu/tree_plugin.h2
-rw-r--r--kernel/rcu/tree_stall.h36
13 files changed, 1250 insertions, 133 deletions
diff --git a/kernel/rcu/Kconfig b/kernel/rcu/Kconfig
index 332df7a7a634..bc9483c0b5d8 100644
--- a/kernel/rcu/Kconfig
+++ b/kernel/rcu/Kconfig
@@ -175,6 +175,12 @@ config RCU_STALL_COMMON
config RCU_NEED_SEGCBLIST
def_bool ( TREE_RCU || TREE_SRCU || TASKS_RCU_GENERIC )
+# The deferral (and the IRQ_WORK it uses) is only needed where call_rcu() /
+# call_srcu() can be invoked while a callback-list operation is in flight.
+config RCU_DEFER
+ def_bool HAVE_NMI || KPROBES || FUNCTION_TRACER || TRACEPOINTS
+ select IRQ_WORK
+
config RCU_FANOUT
int "Tree-based hierarchical RCU fanout value"
range 2 64 if 64BIT
diff --git a/kernel/rcu/rcu.h b/kernel/rcu/rcu.h
index 39a9f6fa9a7b..d60444bf3a02 100644
--- a/kernel/rcu/rcu.h
+++ b/kernel/rcu/rcu.h
@@ -572,6 +572,20 @@ static inline void tasks_cblist_init_generic(void) { }
#define RCU_SCHEDULER_INIT 1
#define RCU_SCHEDULER_RUNNING 2
+/*
+ * Defer whenever interrupts are disabled, since a callback-list operation may
+ * be in flight on this CPU. Not before the scheduler is up: irq_work is not
+ * usable that early, and rcu_init() itself calls call_rcu().
+ */
+static inline bool should_rcu_defer(void)
+{
+ return IS_ENABLED(CONFIG_RCU_DEFER) && irqs_disabled() &&
+ rcu_scheduler_active != RCU_SCHEDULER_INACTIVE;
+}
+
+/* Drain an outgoing CPU's deferred SRCU callbacks; see rcutree_migrate_callbacks(). */
+void srcu_offline_drain(int cpu);
+
enum rcutorture_type {
RCU_FLAVOR,
RCU_TASKS_FLAVOR,
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index 794937e13e7c..968343be4e0d 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -48,6 +48,7 @@
#include <linux/tick.h>
#include <linux/rcupdate_trace.h>
#include <linux/nmi.h>
+#include <linux/perf_event.h>
#include "rcu.h"
@@ -115,6 +116,7 @@ torture_param(int, leakpointer, 0, "Leak pointer dereferences from readers");
torture_param(int, n_barrier_cbs, 0, "# of callbacks/kthreads for barrier testing");
torture_param(int, n_up_down, 32, "# of concurrent up/down hrtimer-based RCU readers");
torture_param(int, nfakewriters, 4, "Number of RCU fake writer threads");
+torture_param(bool, nmi_calls, true, "Exercise ->call() from NMI on nmi_capable flavors");
torture_param(int, nreaders, -1, "Number of RCU reader threads");
torture_param(bool, nwriters, 1, "Number of RCU writer threads (0 or 1)");
torture_param(int, object_debug, 0, "Enable debug-object double call_rcu() testing");
@@ -216,6 +218,8 @@ static long n_rcu_torture_boost_failure;
static long n_rcu_torture_boosts;
static atomic_long_t n_rcu_torture_timers;
static atomic_long_t n_rcu_torture_irqs;
+static atomic_long_t n_rcu_torture_nmi_call;
+static atomic_long_t n_rcu_torture_nmi_cb;
static long n_barrier_attempts;
static long n_barrier_successes; /* did rcu_barrier test succeed? */
static unsigned long n_read_exits;
@@ -433,6 +437,7 @@ struct rcu_torture_ops {
bool (*is_task_rcu_boosted)(void);
long cbflood_max;
int irq_capable;
+ int nmi_capable;
int can_boost;
int extendables;
int slow_gps;
@@ -440,6 +445,7 @@ struct rcu_torture_ops {
int debug_objects;
int start_poll_irqsoff;
int have_up_down;
+ int rdrs_handle_load;
const char *name;
};
@@ -648,6 +654,8 @@ static struct rcu_torture_ops rcu_ops = {
.extendables = RCUTORTURE_MAX_EXTEND,
.debug_objects = 1,
.start_poll_irqsoff = 1,
+ .rdrs_handle_load = !IS_ENABLED(CONFIG_PREEMPT_RCU) || IS_ENABLED(CONFIG_RCU_BOOST),
+ .nmi_capable = 1,
.name = "rcu"
};
@@ -702,10 +710,21 @@ static struct rcu_torture_ops rcu_busted_ops = {
DEFINE_STATIC_SRCU(srcu_ctl);
DEFINE_STATIC_SRCU_FAST(srcu_ctlf);
DEFINE_STATIC_SRCU_FAST_UPDOWN(srcu_ctlfud);
+DEFINE_STATIC_SRCU_ATOMIC(srcu_ctla);
static struct srcu_struct srcu_ctld;
static struct srcu_struct *srcu_ctlp = &srcu_ctl;
static struct rcu_torture_ops srcud_ops;
+// Restrict APIs permitted for atomic SRCU.
+static void srcu_torture_init_forbidden_apis(void)
+{
+ cur_ops->call = NULL;
+ cur_ops->cb_barrier = NULL;
+ cur_ops->deferred_free = NULL;
+ cur_ops->exp_current = NULL;
+ cur_ops->start_gp_poll = NULL;
+}
+
static void srcu_torture_init(void)
{
rcu_sync_torture_init();
@@ -721,6 +740,11 @@ static void srcu_torture_init(void)
srcu_ctlp = &srcu_ctlfud;
VERBOSE_TOROUT_STRING("srcu_torture_init fast-up/down SRCU");
}
+ if (reader_flavor & SRCU_READ_FLAVOR_ATOMIC) {
+ srcu_ctlp = &srcu_ctla;
+ VERBOSE_TOROUT_STRING("srcu_torture_init atomic SRCU");
+ srcu_torture_init_forbidden_apis();
+ }
}
static void srcu_get_gp_data(int *flags, unsigned long *gp_seq)
@@ -758,6 +782,11 @@ static int srcu_torture_read_lock(void)
WARN_ON_ONCE(idx & ~0x1);
ret += idx << 3;
}
+ if (reader_flavor & SRCU_READ_FLAVOR_ATOMIC) {
+ idx = srcu_read_lock_atomic(srcu_ctlp);
+ WARN_ON_ONCE(idx & ~0x1);
+ ret += idx << 4;
+ }
return ret;
}
@@ -777,7 +806,8 @@ srcu_read_delay(struct torture_random_state *rrsp, struct rt_read_seg *rtrsp)
delay = torture_random(rrsp) %
(nrealreaders * 2 * longdelay * uspertick);
- if (!delay && !in_atomic() && !rcu_preempt_depth() && !irqs_disabled()) {
+ if (!delay && !in_atomic() && !rcu_preempt_depth() && !irqs_disabled() &&
+ !(reader_flavor & SRCU_READ_FLAVOR_ATOMIC)) {
schedule_timeout_interruptible(longdelay);
rtrsp->rt_delay_jiffies = longdelay;
} else {
@@ -788,6 +818,8 @@ srcu_read_delay(struct torture_random_state *rrsp, struct rt_read_seg *rtrsp)
static void srcu_torture_read_unlock(int idx)
{
WARN_ON_ONCE((reader_flavor && (idx & ~reader_flavor)) || (!reader_flavor && (idx & ~0x1)));
+ if (reader_flavor & SRCU_READ_FLAVOR_ATOMIC)
+ srcu_read_unlock_atomic(srcu_ctlp, (idx & 0x10) >> 4);
if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN)
srcu_read_unlock_fast_updown(srcu_ctlp,
__srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3));
@@ -867,7 +899,10 @@ static void srcu_torture_deferred_free(struct rcu_torture *rp)
static void srcu_torture_synchronize(void)
{
- synchronize_srcu(srcu_ctlp);
+ if (reader_flavor & SRCU_READ_FLAVOR_ATOMIC)
+ synchronize_srcu_atomic(srcu_ctlp);
+ else
+ synchronize_srcu(srcu_ctlp);
}
static unsigned long srcu_torture_get_gp_state(void)
@@ -903,7 +938,10 @@ static void srcu_torture_stats(void)
static void srcu_torture_synchronize_expedited(void)
{
- synchronize_srcu_expedited(srcu_ctlp);
+ if (reader_flavor & SRCU_READ_FLAVOR_ATOMIC)
+ synchronize_srcu_atomic(srcu_ctlp);
+ else
+ synchronize_srcu_expedited(srcu_ctlp);
}
static void srcu_torture_expedite_current(void)
@@ -942,6 +980,7 @@ static struct rcu_torture_ops srcu_ops = {
.debug_objects = 1,
.have_up_down = IS_ENABLED(CONFIG_TINY_SRCU)
? 0 : SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_FAST_UPDOWN,
+ .nmi_capable = 1,
.name = "srcu"
};
@@ -960,6 +999,10 @@ static void srcud_torture_init(void)
} else if (reader_flavor & SRCU_READ_FLAVOR_FAST_UPDOWN) {
WARN_ON(init_srcu_struct_fast_updown(&srcu_ctld));
VERBOSE_TOROUT_STRING("srcud_torture_init fast-up/down SRCU");
+ } else if (reader_flavor & SRCU_READ_FLAVOR_ATOMIC) {
+ WARN_ON(init_srcu_struct_atomic(&srcu_ctld));
+ VERBOSE_TOROUT_STRING("srcud_torture_init atomic SRCU");
+ srcu_torture_init_forbidden_apis();
} else {
WARN_ON(init_srcu_struct(&srcu_ctld));
}
@@ -1005,6 +1048,7 @@ static struct rcu_torture_ops srcud_ops = {
.debug_objects = 1,
.have_up_down = IS_ENABLED(CONFIG_TINY_SRCU)
? 0 : SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_FAST_UPDOWN,
+ .nmi_capable = 1,
.name = "srcud"
};
@@ -1269,6 +1313,7 @@ static struct rcu_torture_ops tasks_tracing_ops = {
.cbflood_max = 50000,
.irq_capable = 1,
.slow_gps = 1,
+ .nmi_capable = 1,
.name = "tasks-tracing"
};
@@ -1738,7 +1783,7 @@ rcu_torture_writer(void *arg)
pr_alert("%s" TORTURE_FLAG " Waited %lu jiffies for boot to complete.\n",
torture_type, jiffies - j);
- if (IS_ENABLED(CONFIG_RCU_LAZY))
+ if (IS_ENABLED(CONFIG_RCU_LAZY) && cur_ops->call)
INIT_WORK_ONSTACK(&lazy_work, rcu_torture_writer_work);
do {
@@ -1933,7 +1978,7 @@ rcu_torture_writer(void *arg)
!rcu_gp_is_normal();
}
rcu_torture_writer_state = RTWS_STUTTER;
- if (IS_ENABLED(CONFIG_RCU_LAZY))
+ if (IS_ENABLED(CONFIG_RCU_LAZY) && cur_ops->call)
queue_work(system_percpu_wq, &lazy_work);
stutter_waited = stutter_wait("rcu_torture_writer");
if (stutter_waited &&
@@ -1966,7 +2011,7 @@ rcu_torture_writer(void *arg)
" Dynamic grace-period expediting was disabled.\n",
torture_type);
- if (IS_ENABLED(CONFIG_RCU_LAZY)) {
+ if (IS_ENABLED(CONFIG_RCU_LAZY) && cur_ops->call) {
cancel_work_sync(&lazy_work);
destroy_work_on_stack(&lazy_work);
}
@@ -2534,8 +2579,10 @@ static bool rcu_torture_one_read_start(struct rcu_torture_one_read_state *rtorsp
rtorsp->p = rcu_dereference_check(rcu_torture_current,
!cur_ops->readlock_held || cur_ops->readlock_held() ||
(rtorsp->readstate & RCUTORTURE_RDR_UPDOWN));
- if (rtorsp->p == NULL) {
- /* Wait for rcu_torture_writer to get underway */
+ if ((!cur_ops->rdrs_handle_load && atomic_read(&rcu_fwd_cb_nodelay)) || rtorsp->p == NULL) {
+ // Wait for rcu_torture_writer to get underway and
+ // (if readers cannot handle heavy loads) for any
+ // forward-progress testing to complete.
rcutorture_one_extend(&rtorsp->readstate, 0, trsp, rtorsp->rtrsp);
return false;
}
@@ -2660,6 +2707,124 @@ static bool rcu_torture_one_read(struct torture_random_state *trsp, long myid)
static DEFINE_TORTURE_RANDOM_PERCPU(rcu_torture_timer_rand);
/*
+ * Exercise ->call() from NMI context for flavors that set ->nmi_capable. A
+ * per-CPU hardware perf counter overflows into an NMI, and its handler submits
+ * a preallocated callback via ->call(). One callback per CPU is in flight at a
+ * time (guarded by an atomic) to avoid allocating in NMI.
+ */
+#ifdef CONFIG_PERF_EVENTS
+static struct perf_event_attr rcu_torture_nmi_attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_CPU_CYCLES,
+ .size = sizeof(struct perf_event_attr),
+ .pinned = 1,
+ .disabled = 1,
+ /*
+ * A fixed period rather than .freq: a frequency-based event bumps
+ * nr_freq_events, which sets TICK_DEP_BIT_PERF_EVENTS and would pin the
+ * tick for the whole run on NO_HZ_FULL kernels.
+ */
+ .sample_period = 20 * 1000 * 1000,
+};
+
+/* One in-flight callback per CPU; ->inuse is released by the callback. */
+struct rcu_torture_nmi_cb {
+ struct rcu_head rh;
+ atomic_t inuse;
+};
+
+static struct perf_event **rcu_torture_nmi_events;
+static int rcu_torture_nmi_hp_state;
+static DEFINE_PER_CPU(struct rcu_torture_nmi_cb, rcu_torture_nmi_cb);
+
+static void rcu_torture_nmi_invoked(struct rcu_head *rhp)
+{
+ struct rcu_torture_nmi_cb *rtncp = container_of(rhp, struct rcu_torture_nmi_cb, rh);
+
+ atomic_long_inc(&n_rcu_torture_nmi_cb);
+ atomic_set(&rtncp->inuse, 0);
+}
+
+static void rcu_torture_nmi_overflow(struct perf_event *event,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct rcu_torture_nmi_cb *rtncp = this_cpu_ptr(&rcu_torture_nmi_cb);
+
+ if (!in_nmi())
+ return;
+ if (cur_ops->call && !atomic_xchg(&rtncp->inuse, 1)) {
+ atomic_long_inc(&n_rcu_torture_nmi_call);
+ cur_ops->call(&rtncp->rh, rcu_torture_nmi_invoked);
+ }
+}
+
+static int rcu_torture_nmi_online(unsigned int cpu)
+{
+ struct perf_event *event;
+
+ event = perf_event_create_kernel_counter(&rcu_torture_nmi_attr, cpu, NULL,
+ rcu_torture_nmi_overflow, NULL);
+ if (IS_ERR(event))
+ return 0;
+ rcu_torture_nmi_events[cpu] = event;
+ perf_event_enable(event);
+ return 0;
+}
+
+static int rcu_torture_nmi_offline(unsigned int cpu)
+{
+ struct perf_event *event = rcu_torture_nmi_events[cpu];
+
+ if (event) {
+ rcu_torture_nmi_events[cpu] = NULL;
+ perf_event_disable(event);
+ perf_event_release_kernel(event);
+ }
+ return 0;
+}
+
+/* Drive the counters from hotplug callbacks so coverage survives onoff. */
+static void rcu_torture_nmi_init(void)
+{
+ int ret;
+
+ if (!nmi_calls || !cur_ops->nmi_capable || !cur_ops->call)
+ return;
+ rcu_torture_nmi_events = kcalloc(nr_cpu_ids, sizeof(*rcu_torture_nmi_events),
+ GFP_KERNEL);
+ if (!rcu_torture_nmi_events)
+ return;
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "rcutorture/nmi:online",
+ rcu_torture_nmi_online, rcu_torture_nmi_offline);
+ if (ret < 0) {
+ kfree(rcu_torture_nmi_events);
+ rcu_torture_nmi_events = NULL;
+ return;
+ }
+ rcu_torture_nmi_hp_state = ret;
+}
+
+static void rcu_torture_nmi_cleanup(void)
+{
+ if (!rcu_torture_nmi_events)
+ return;
+ if (rcu_torture_nmi_hp_state > 0) {
+ cpuhp_remove_state(rcu_torture_nmi_hp_state);
+ rcu_torture_nmi_hp_state = 0;
+ }
+ kfree(rcu_torture_nmi_events);
+ rcu_torture_nmi_events = NULL;
+ if (!atomic_long_read(&n_rcu_torture_nmi_call))
+ pr_alert("%s: nmi_calls set but no ->call() ever issued from NMI, so NMI ->call() went untested (no PMU, or NMIs unavailable here).\n",
+ __func__);
+}
+#else /* #ifdef CONFIG_PERF_EVENTS */
+static void rcu_torture_nmi_init(void) { }
+static void rcu_torture_nmi_cleanup(void) { }
+#endif /* #else #ifdef CONFIG_PERF_EVENTS */
+
+/*
* RCU torture reader from timer handler. Dereferences rcu_torture_current,
* incrementing the corresponding element of the pipeline array. The
* counter in the element should never be greater than 1, otherwise, the
@@ -3047,6 +3212,9 @@ rcu_torture_stats_print(void)
data_race(n_barrier_attempts),
data_race(n_rcu_torture_barrier_error));
pr_cont("read-exits: %ld ", data_race(n_read_exits)); // Statistic.
+ pr_cont("nmi-calls: %ld nmi-cbs: %ld ",
+ atomic_long_read(&n_rcu_torture_nmi_call),
+ atomic_long_read(&n_rcu_torture_nmi_cb));
pr_cont("nocb-toggles: %ld:%ld ",
atomic_long_read(&n_nocb_offload), atomic_long_read(&n_nocb_deoffload));
pr_cont("gpwraps: %ld\n", n_gpwraps);
@@ -3195,7 +3363,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
"read_exit_delay=%d read_exit_burst=%d "
"reader_flavor=%x "
"nocbs_nthreads=%d nocbs_toggle=%d "
- "test_nmis=%d "
+ "test_nmis=%d nmi_calls=%d "
"preempt_duration=%d preempt_interval=%d n_up_down=%d\n",
torture_type, tag, nrealreaders, nwriters, nrealfakewriters,
stat_interval, verbose, test_no_idle_hz, shuffle_interval,
@@ -3209,7 +3377,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
read_exit_delay, read_exit_burst,
reader_flavor,
nocbs_nthreads, nocbs_toggle,
- test_nmis,
+ test_nmis, nmi_calls,
preempt_duration, preempt_interval, n_up_down);
}
@@ -3856,7 +4024,7 @@ static int __init rcu_torture_fwd_prog_init(void)
}
if (fwd_progress_holdoff <= 0)
fwd_progress_holdoff = 1;
- if (fwd_progress_div <= 0)
+ if (fwd_progress_div < 2)
fwd_progress_div = 4;
rfp = kzalloc_objs(*rfp, fwd_progress);
fwd_prog_tasks = kzalloc_objs(*fwd_prog_tasks, fwd_progress);
@@ -4283,6 +4451,7 @@ rcu_torture_cleanup(void)
int i;
if (torture_cleanup_begin()) {
+ rcu_torture_nmi_cleanup();
if (cur_ops->cb_barrier != NULL) {
pr_info("%s: Invoking %pS().\n", __func__, cur_ops->cb_barrier);
cur_ops->cb_barrier();
@@ -4322,9 +4491,12 @@ rcu_torture_cleanup(void)
for (i = 0; i < nrealreaders; i++)
torture_stop_kthread(rcu_torture_reader,
reader_tasks[i]);
+ if (irqreader && cur_ops->irq_capable)
+ kick_all_cpus_sync();
kfree(reader_tasks);
reader_tasks = NULL;
}
+ rcu_torture_nmi_cleanup();
kfree(rcu_torture_reader_mbchk);
rcu_torture_reader_mbchk = NULL;
@@ -4354,6 +4526,19 @@ rcu_torture_cleanup(void)
pr_info("%s: Invoking %pS().\n", __func__, cur_ops->cb_barrier);
cur_ops->cb_barrier();
}
+
+ /*
+ * cb_barrier() above drained every deferred callback, so the count
+ * issued from NMI must equal the count invoked.
+ */
+ if (atomic_long_read(&n_rcu_torture_nmi_call) !=
+ atomic_long_read(&n_rcu_torture_nmi_cb)) {
+ pr_alert("%s: NMI ->call() lost a callback: issued %ld invoked %ld\n",
+ __func__, atomic_long_read(&n_rcu_torture_nmi_call),
+ atomic_long_read(&n_rcu_torture_nmi_cb));
+ atomic_inc(&n_rcu_torture_error);
+ }
+
if (cur_ops->cleanup != NULL)
cur_ops->cleanup();
@@ -4477,6 +4662,17 @@ static DECLARE_RWSEM(rwsem7);
static DECLARE_RWSEM(rwsem8);
static DECLARE_RWSEM(rwsem9);
+static DEFINE_RAW_SPINLOCK(spin0);
+static DEFINE_RAW_SPINLOCK(spin1);
+static DEFINE_RAW_SPINLOCK(spin2);
+static DEFINE_RAW_SPINLOCK(spin3);
+static DEFINE_RAW_SPINLOCK(spin4);
+static DEFINE_RAW_SPINLOCK(spin5);
+static DEFINE_RAW_SPINLOCK(spin6);
+static DEFINE_RAW_SPINLOCK(spin7);
+static DEFINE_RAW_SPINLOCK(spin8);
+static DEFINE_RAW_SPINLOCK(spin9);
+
DEFINE_STATIC_SRCU(srcu0);
DEFINE_STATIC_SRCU(srcu1);
DEFINE_STATIC_SRCU(srcu2);
@@ -4488,6 +4684,19 @@ DEFINE_STATIC_SRCU(srcu7);
DEFINE_STATIC_SRCU(srcu8);
DEFINE_STATIC_SRCU(srcu9);
+DEFINE_STATIC_SRCU_ATOMIC(srcu0_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu1_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu2_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu3_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu4_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu5_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu6_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu7_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu8_atomic);
+DEFINE_STATIC_SRCU_ATOMIC(srcu9_atomic);
+
+DEFINE_STATIC_SRCU_ATOMIC(srcu_irq);
+
static int srcu_lockdep_next(const char *f, const char *fl, const char *fs, const char *fu, int i,
int cyclelen, int deadlock)
{
@@ -4502,6 +4711,11 @@ static int srcu_lockdep_next(const char *f, const char *fl, const char *fs, cons
return j;
}
+static void srcu_sync_irq(void *unused)
+{
+ synchronize_srcu_atomic(&srcu_irq);
+}
+
// Test lockdep on SRCU-based deadlock scenarios.
static void rcu_torture_init_srcu_lockdep(void)
{
@@ -4517,6 +4731,12 @@ static void rcu_torture_init_srcu_lockdep(void)
&rwsem5, &rwsem6, &rwsem7, &rwsem8, &rwsem9 };
struct srcu_struct *srcus[] = { &srcu0, &srcu1, &srcu2, &srcu3, &srcu4,
&srcu5, &srcu6, &srcu7, &srcu8, &srcu9 };
+ raw_spinlock_t *spins[] = { &spin0, &spin1, &spin2, &spin3, &spin4,
+ &spin5, &spin6, &spin7, &spin8, &spin9 };
+ struct srcu_struct *srcus_atomic[] = { &srcu0_atomic, &srcu1_atomic, &srcu2_atomic,
+ &srcu3_atomic, &srcu4_atomic, &srcu5_atomic,
+ &srcu6_atomic, &srcu7_atomic, &srcu8_atomic,
+ &srcu9_atomic };
int testtype;
if (!test_srcu_lockdep)
@@ -4627,11 +4847,83 @@ static void rcu_torture_init_srcu_lockdep(void)
}
#endif // #ifdef CONFIG_TASKS_TRACE_RCU
+ if (testtype == 4) {
+ pr_info("%s: test_srcu_lockdep = %05d: SRCU_ATOMIC %d-way %sdeadlock.\n",
+ __func__, test_srcu_lockdep, cyclelen, deadlock ? "" : "non-");
+ if (deadlock && cyclelen == 1)
+ pr_info("%s: Expect hang.\n", __func__);
+ for (i = 0; i < cyclelen; i++) {
+ j = srcu_lockdep_next(__func__, "srcu_read_lock_atomic",
+ "synchronize_srcu_atomic",
+ "srcu_read_unlock_atomic", i,
+ cyclelen, deadlock);
+ idx = srcu_read_lock_atomic(srcus_atomic[i]);
+ if (j >= 0)
+ synchronize_srcu_atomic(srcus_atomic[j]);
+ srcu_read_unlock_atomic(srcus_atomic[i], idx);
+ }
+ return;
+ }
+
+ if (testtype == 5) {
+ pr_info("%s: test_srcu_lockdep = %05d: SRCU_ATOMIC/raw_spinlock %d-way %sdeadlock.\n",
+ __func__, test_srcu_lockdep, cyclelen, deadlock ? "" : "non-");
+ for (i = 0; i < cyclelen; i++) {
+ pr_info("%s: srcu_read_lock_atomic(%d), raw_spin_lock(%d), raw_spin_unlock(%d), srcu_read_unlock_atomic(%d)\n",
+ __func__, i, i, i, i);
+ idx = srcu_read_lock_atomic(srcus_atomic[i]);
+ raw_spin_lock(spins[i]);
+ raw_spin_unlock(spins[i]);
+ srcu_read_unlock_atomic(srcus_atomic[i], idx);
+
+ j = srcu_lockdep_next(__func__, "raw_spin_lock",
+ "synchronize_srcu_atomic",
+ "raw_spin_unlock", i, cyclelen,
+ deadlock);
+ raw_spin_lock(spins[i]);
+ if (j >= 0)
+ synchronize_srcu_atomic(srcus_atomic[j]);
+ raw_spin_unlock(spins[i]);
+ }
+ return;
+ }
+
+ if (testtype == 6) {
+ pr_info("%s: test_srcu_lockdep = %05d: synchronize_srcu_atomic() inside rcu_read_lock() %d-way.\n",
+ __func__, test_srcu_lockdep, cyclelen);
+ for (i = 0; i < cyclelen; i++) {
+ rcu_read_lock();
+ synchronize_srcu_atomic(srcus_atomic[i]);
+ rcu_read_unlock();
+ }
+ return;
+ }
+
+ if (testtype == 7) {
+ int cpu;
+
+ for (i = 0; i < cyclelen; i++) {
+ idx = srcu_read_lock_atomic(&srcu_irq);
+ cpu = cpumask_any_but(cpu_online_mask,
+ smp_processor_id());
+ if (cpu < nr_cpu_ids) {
+ pr_info("%s: CPU%d sending IPI to CPU%d\n",
+ __func__, smp_processor_id(), cpu);
+ smp_call_function_single(cpu, srcu_sync_irq,
+ NULL, 1);
+ }
+ srcu_read_unlock_atomic(&srcu_irq, idx);
+ }
+ return;
+ }
+
err_out:
pr_info("%s: test_srcu_lockdep = %05d does nothing.\n", __func__, test_srcu_lockdep);
pr_info("%s: test_srcu_lockdep = DNNL.\n", __func__);
pr_info("%s: D: Deadlock if nonzero.\n", __func__);
- pr_info("%s: NN: Test number, 0=SRCU, 1=SRCU/mutex, 2=SRCU/rwsem, 3=SRCU/Tasks Trace RCU.\n", __func__);
+ pr_info("%s: NN: Test number, 0=SRCU, 1=SRCU/mutex, 2=SRCU/rwsem, 3=SRCU/Tasks Trace RCU, 4=SRCU_ATOMIC, ",
+ __func__);
+ pr_cont("5=SRCU_ATOMIC/raw_spinlock, 6=synchronize_srcu_atomic inside rcu_read_lock, 7=atomic SRCU cross-CPU IRQ context mismatch.\n");
pr_info("%s: L: Cycle length.\n", __func__);
if (!IS_ENABLED(CONFIG_TASKS_TRACE_RCU))
pr_info("%s: NN=3 disallowed because kernel is built with CONFIG_TASKS_TRACE_RCU=n\n", __func__);
@@ -4786,6 +5078,7 @@ rcu_torture_init(void)
firsterr = -ENOMEM;
goto unwind;
}
+ rcu_torture_nmi_init();
for (i = 0; i < nrealreaders; i++) {
rcu_torture_reader_mbchk[i].rtc_chkrdr = -1;
firsterr = torture_create_kthread(rcu_torture_reader, (void *)i,
diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
index 558ba8d316db..63a3f0a8bc53 100644
--- a/kernel/rcu/srcutiny.c
+++ b/kernel/rcu/srcutiny.c
@@ -10,6 +10,7 @@
#include <linux/export.h>
#include <linux/irq_work.h>
+#include <linux/llist.h>
#include <linux/mutex.h>
#include <linux/preempt.h>
#include <linux/rcupdate_wait.h>
@@ -29,6 +30,8 @@ extern int rcu_scheduler_active;
static LIST_HEAD(srcu_boot_list);
static bool srcu_init_done;
+static void __srcu_defer_drain(struct srcu_struct *ssp);
+
static int init_srcu_struct_fields(struct srcu_struct *ssp)
{
ssp->srcu_lock_nesting[0] = 0;
@@ -38,11 +41,14 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp)
ssp->srcu_cb_tail = &ssp->srcu_cb_head;
ssp->srcu_gp_running = false;
ssp->srcu_gp_waiting = false;
+ ssp->srcu_atomic_gp_flag = 0;
ssp->srcu_idx = 0;
ssp->srcu_idx_max = 0;
INIT_WORK(&ssp->srcu_work, srcu_drive_gp);
INIT_LIST_HEAD(&ssp->srcu_work.entry);
init_irq_work(&ssp->srcu_irq_work, srcu_tiny_irq_work);
+ init_llist_head(&ssp->defer_cbs);
+ ssp->defer_iw = IRQ_WORK_INIT_HARD(srcu_defer_drain);
return 0;
}
@@ -86,6 +92,16 @@ EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
void cleanup_srcu_struct(struct srcu_struct *ssp)
{
WARN_ON(srcu_readers_active(ssp));
+ /*
+ * Re-issue any deferred callbacks, then wait out ->defer_iw before it is
+ * freed. Skipped entirely with CONFIG_RCU_DEFER=n: irq_work_sync() ends
+ * in an unconditional synchronize_rcu() wherever
+ * arch_irq_work_has_interrupt() is false, which is every !SMP target.
+ */
+ if (IS_ENABLED(CONFIG_RCU_DEFER)) {
+ __srcu_defer_drain(ssp);
+ irq_work_sync(&ssp->defer_iw);
+ }
irq_work_sync(&ssp->srcu_irq_work);
flush_work(&ssp->srcu_work);
WARN_ON(ssp->srcu_gp_running);
@@ -144,6 +160,8 @@ void srcu_drive_gp(struct work_struct *wp)
WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1);
WRITE_ONCE(ssp->srcu_gp_waiting, true); /* srcu_read_unlock() wakes! */
preempt_enable();
+ if (IS_ENABLED(CONFIG_PREEMPTION))
+ synchronize_rcu(); // Needed for RCU Tasks Trace to imply RCU grace period
do {
// Deadlock issues prevent __srcu_read_unlock() from
// doing an unconditional wakeup, so polling is required.
@@ -213,11 +231,11 @@ static void srcu_gp_start_if_needed(struct srcu_struct *ssp)
}
/*
- * Enqueue an SRCU callback on the specified srcu_struct structure,
- * initiating grace-period processing if it is not already running.
+ * Also called by __srcu_defer_drain() to re-issue a deferred callback, so it
+ * must not re-check the deferral condition.
*/
-void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
- rcu_callback_t func)
+static void srcu_do_enqueue(struct srcu_struct *ssp, struct rcu_head *rhp,
+ rcu_callback_t func)
{
unsigned long flags;
@@ -231,6 +249,68 @@ void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
srcu_gp_start_if_needed(ssp);
preempt_enable();
}
+
+/*
+ * Set only by the irq_work drain, the one drain its own re-issue can re-feed;
+ * a callback staged during a direct drain is taken by ->defer_iw afterwards.
+ * Global rather than per-srcu_struct: a re-entrant call_srcu(B) inside a drain
+ * of A raises B's own ->defer_iw, whose drain can stage back onto A.
+ */
+static bool srcu_defer_draining;
+
+static void __srcu_defer_drain(struct srcu_struct *ssp)
+{
+ struct llist_node *node, *next;
+ unsigned long flags;
+
+ if (!IS_ENABLED(CONFIG_RCU_DEFER))
+ return;
+
+ /* Re-issued newest-first; nothing depends on call_srcu() ordering. */
+ local_irq_save(flags);
+ llist_for_each_safe(node, next, llist_del_all(&ssp->defer_cbs)) {
+ struct rcu_head *rhp = (struct rcu_head *)node;
+
+ srcu_do_enqueue(ssp, rhp, rhp->func);
+ }
+ local_irq_restore(flags);
+}
+
+/* Only the irq_work drain can be re-fed by its own re-issue; see Tree SRCU. */
+void srcu_defer_drain(struct irq_work *iw)
+{
+ struct srcu_struct *ssp = container_of(iw, struct srcu_struct, defer_iw);
+
+ WRITE_ONCE(srcu_defer_draining, true);
+ __srcu_defer_drain(ssp);
+ WRITE_ONCE(srcu_defer_draining, false);
+}
+EXPORT_SYMBOL_GPL(srcu_defer_drain);
+
+void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
+ rcu_callback_t func)
+{
+ if (should_rcu_defer()) {
+ /* A re-entrant call_srcu() during the drain would livelock it. */
+ if (READ_ONCE(srcu_defer_draining) && !in_nmi()) {
+ WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU),
+ "call_srcu() re-entered during callback drain; leaking callback\n");
+ return;
+ }
+ rhp->func = func;
+ if (llist_add((struct llist_node *)rhp, &ssp->defer_cbs))
+ irq_work_queue(&ssp->defer_iw);
+ return;
+ }
+
+ /*
+ * Only reachable from an NMI when deferral is off: before the scheduler
+ * is up, or with CONFIG_RCU_DEFER=n. The enqueue can then race.
+ */
+ WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi());
+
+ srcu_do_enqueue(ssp, rhp, func);
+}
EXPORT_SYMBOL_GPL(call_srcu);
/*
@@ -261,6 +341,102 @@ void synchronize_srcu(struct srcu_struct *ssp)
EXPORT_SYMBOL_GPL(synchronize_srcu);
/*
+ * synchronize_srcu_atomic - spinning grace period for atomic-reader domains
+ * @ssp: srcu_struct with which to synchronize.
+ *
+ * On !SMP this cannot spin: a reader observed mid-section is preempted
+ * or interrupted-out, and can only finish if we yield the CPU. But it
+ * is also never needed: an atomic-flavor reader (preemption disabled)
+ * cannot be observed mid-section from process context on the sole CPU.
+ * So a reader observed here has broken the atomic-domain promise, and
+ * the only correct wait for it is a real grace period.
+ *
+ * (Actual kernel-doc header is in Tree SRCU.)
+ */
+void synchronize_srcu_atomic(struct srcu_struct *ssp)
+{
+ int idx;
+ bool ret;
+ unsigned long srcu_state = get_state_synchronize_srcu(ssp);
+
+ srcu_lock_sync(&ssp->dep_map);
+
+ RCU_LOCKDEP_WARN(lockdep_is_held(ssp),
+ "Illegal synchronize_srcu_atomic() in same-type SRCU read-side critical section");
+
+ if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
+ return;
+
+ if (IS_ENABLED(CONFIG_PREEMPTION))
+ synchronize_rcu(); // Needed for RCU Tasks Trace to imply RCU grace period.
+ // And in Tiny RCU, it is near zero cost and doesn't block.
+
+ // Usually, there will be no readers.
+ preempt_disable(); // Guard against lazy preemption and some other grace period.
+ ret = !READ_ONCE(ssp->srcu_lock_nesting[0]) && !READ_ONCE(ssp->srcu_lock_nesting[1]);
+ if (ret) {
+ WRITE_ONCE(ssp->srcu_idx_max, ssp->srcu_idx + 2);
+ WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 2);
+ preempt_enable();
+ return;
+ }
+
+ // Because readers disable preemption, we should never get here.
+ // However, a splat and some spinning is usually preferable to
+ // memory corruption due to a too-short grace period. There is
+ // the possibility that this will hang if the preempted reader is
+ // not looked upon favorably by the scheduler, but this is still
+ // preferable to memory corruption.
+ WARN_ON_ONCE(1);
+
+ // Wait to drive a grace period or for someone else to do it
+ // for us while we are lazily preempted.
+ while (ssp->srcu_atomic_gp_flag) {
+ if (poll_state_synchronize_srcu(ssp, srcu_state)) {
+ preempt_enable();
+ return;
+ }
+ preempt_enable();
+ cpu_relax();
+ cond_resched_tasks_rcu_qs();
+ preempt_disable();
+ }
+ ssp->srcu_atomic_gp_flag = 1; // Acquire SRCU grace-period "lock".
+ preempt_enable();
+
+ // We get here if a reader has been lazily preempted.
+ // First, wait for old readers, which are quite unlikely.
+ WRITE_ONCE(ssp->srcu_idx_max, get_state_synchronize_srcu(ssp));
+ idx = !(((READ_ONCE(ssp->srcu_idx) + 1) & 0x2) >> 1);
+ while (READ_ONCE(ssp->srcu_lock_nesting[idx])) {
+ cond_resched_tasks_rcu_qs();
+ cpu_relax();
+ }
+
+ // Next, flip the index and wait for the other group of readers.
+ WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1);
+ idx = !idx;
+ while (READ_ONCE(ssp->srcu_lock_nesting[idx])) {
+ cond_resched_tasks_rcu_qs();
+ cpu_relax();
+ }
+
+ // Finally, flip the index again for poll_state_synchronize_srcu().
+ WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1);
+ WARN_ON_ONCE(!poll_state_synchronize_srcu(ssp, srcu_state));
+ ssp->srcu_atomic_gp_flag = 0; // Release SRCU grace-period "lock".
+}
+EXPORT_SYMBOL_GPL(synchronize_srcu_atomic);
+
+/* Register any deferred callbacks, then wait for all in-flight ones. */
+void srcu_barrier(struct srcu_struct *ssp)
+{
+ __srcu_defer_drain(ssp);
+ synchronize_srcu(ssp);
+}
+EXPORT_SYMBOL_GPL(srcu_barrier);
+
+/*
* get_state_synchronize_srcu - Provide an end-of-grace-period cookie
*/
unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp)
@@ -280,6 +456,11 @@ EXPORT_SYMBOL_GPL(get_state_synchronize_srcu);
* The difference between this and get_state_synchronize_srcu() is that
* this function ensures that the poll_state_synchronize_srcu() will
* eventually return the value true.
+ *
+ * This function cannot be used with atomic SRCU, which only has
+ * atomic grace periods. Doing so will silently corrupt internal
+ * SRCU state. Tree SRCU has appropriate checking with splats,
+ * so please test with CONFIG_SMP=y as well as CONFIG_SMP=n.
*/
unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp)
{
diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
index ad27880dd690..2b0e2b2d2c18 100644
--- a/kernel/rcu/srcutree.c
+++ b/kernel/rcu/srcutree.c
@@ -20,6 +20,7 @@
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <linux/irq_work.h>
+#include <linux/llist.h>
#include <linux/rcupdate_wait.h>
#include <linux/sched.h>
#include <linux/smp.h>
@@ -79,6 +80,38 @@ static void process_srcu(struct work_struct *work);
static void srcu_irq_work(struct irq_work *work);
static void srcu_delay_timer(struct timer_list *t);
+struct srcu_defer;
+static void srcu_defer_drain(struct irq_work *iw);
+static void __srcu_defer_drain(struct srcu_defer *sndp);
+
+/*
+ * Per-CPU call_srcu() deferral state, shared by every srcu_struct. A deferred
+ * callback is staged on its srcu_data's ->defer_cbs; that srcu_data is chained
+ * via ->defer_link onto ->list, which the irq_work walks.
+ */
+struct srcu_defer {
+ struct llist_head list;
+ struct irq_work iw;
+ raw_spinlock_t lock;
+ bool draining;
+};
+
+static DEFINE_PER_CPU(struct srcu_defer, srcu_defer) = {
+ .lock = __RAW_SPIN_LOCK_UNLOCKED(srcu_defer.lock),
+ .iw = IRQ_WORK_INIT_HARD(srcu_defer_drain),
+};
+
+/*
+ * Flush pending deferred callbacks so a following srcu_barrier() waits for them.
+ */
+static void srcu_defer_flush(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ __srcu_defer_drain(&per_cpu(srcu_defer, cpu));
+}
+
/*
* Initialize SRCU per-CPU data. Note that statically allocated
* srcu_struct structures might already have srcu_read_lock() and
@@ -107,6 +140,11 @@ static void init_srcu_struct_data(struct srcu_struct *ssp)
sdp->cpu = cpu;
INIT_WORK(&sdp->work, srcu_invoke_callbacks);
timer_setup(&sdp->delay_work, srcu_delay_timer, 0);
+ /*
+ * ->defer_cbs and ->defer_link are valid when zeroed and are not
+ * reinitialized here: that would clobber callbacks a reentrant
+ * call_srcu() already staged. See __call_srcu().
+ */
sdp->ssp = ssp;
}
}
@@ -200,8 +238,10 @@ static bool init_srcu_struct_nodes(struct srcu_struct *ssp, gfp_t gfp_flags)
* Initialize non-compile-time initialized fields, including the
* associated srcu_node and srcu_data structures. The is_static parameter
* tells us that ->sda has already been wired up to srcu_data.
+ * The is_atomic parameter tells us that there is no reason to
+ * ever transition to big.
*/
-static int init_srcu_struct_fields(struct srcu_struct *ssp, bool is_static)
+static int init_srcu_struct_fields(struct srcu_struct *ssp, bool is_static, bool is_atomic)
{
if (!is_static)
ssp->srcu_sup = kzalloc_obj(*ssp->srcu_sup);
@@ -213,12 +253,18 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp, bool is_static)
ssp->srcu_sup->node = NULL;
mutex_init(&ssp->srcu_sup->srcu_cb_mutex);
mutex_init(&ssp->srcu_sup->srcu_gp_mutex);
+ atomic_set(&ssp->srcu_sup->srcu_atomic_gp_flag, 0);
ssp->srcu_sup->srcu_gp_seq = SRCU_GP_SEQ_INITIAL_VAL;
ssp->srcu_sup->srcu_barrier_seq = 0;
mutex_init(&ssp->srcu_sup->srcu_barrier_mutex);
atomic_set(&ssp->srcu_sup->srcu_barrier_cpu_cnt, 0);
INIT_DELAYED_WORK(&ssp->srcu_sup->work, process_srcu);
- init_irq_work(&ssp->srcu_sup->irq_work, srcu_irq_work);
+ /*
+ * trace events started on the command line require SRCU before
+ * the irq_work kthread starts. Since all it does is a simple
+ * wakeup, having it as a hard irq, even on PREEMPT_RT is fine.
+ */
+ ssp->srcu_sup->irq_work = IRQ_WORK_INIT_HARD(srcu_irq_work);
ssp->srcu_sup->sda_is_static = is_static;
if (!is_static) {
ssp->sda = alloc_percpu(struct srcu_data);
@@ -229,7 +275,8 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp, bool is_static)
init_srcu_struct_data(ssp);
ssp->srcu_sup->srcu_gp_seq_needed_exp = SRCU_GP_SEQ_INITIAL_VAL;
ssp->srcu_sup->srcu_last_gp_end = ktime_get_mono_fast_ns();
- if (READ_ONCE(ssp->srcu_sup->srcu_size_state) == SRCU_SIZE_SMALL && SRCU_SIZING_IS_INIT()) {
+ if (!is_atomic &&
+ READ_ONCE(ssp->srcu_sup->srcu_size_state) == SRCU_SIZE_SMALL && SRCU_SIZING_IS_INIT()) {
if (!preemptible())
WRITE_ONCE(ssp->srcu_sup->srcu_size_state, SRCU_SIZE_ALLOC);
else if (init_srcu_struct_nodes(ssp, GFP_KERNEL))
@@ -258,26 +305,27 @@ err_free_sup:
#ifdef CONFIG_DEBUG_LOCK_ALLOC
static int
-__init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
+__init_srcu_struct_common(struct srcu_struct *ssp, const char *name,
+ struct lock_class_key *key, bool is_atomic)
{
/* Don't re-initialize a lock while it is held. */
debug_check_no_locks_freed((void *)ssp, sizeof(*ssp));
lockdep_init_map(&ssp->dep_map, name, key, 0);
- return init_srcu_struct_fields(ssp, false);
+ return init_srcu_struct_fields(ssp, false, is_atomic);
}
int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
struct lock_class_key *key)
{
ssp->srcu_reader_flavor = 0;
- return __init_srcu_struct_common(ssp, name, key);
+ return __init_srcu_struct_common(ssp, name, key, false);
}
EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
{
ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_FAST;
- return __init_srcu_struct_common(ssp, name, key);
+ return __init_srcu_struct_common(ssp, name, key, false);
}
EXPORT_SYMBOL_GPL(__init_srcu_struct_fast);
@@ -285,10 +333,17 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
struct lock_class_key *key)
{
ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_FAST_UPDOWN;
- return __init_srcu_struct_common(ssp, name, key);
+ return __init_srcu_struct_common(ssp, name, key, false);
}
EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);
+int __init_srcu_struct_atomic(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
+{
+ ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_ATOMIC;
+ return __init_srcu_struct_common(ssp, name, key, true);
+}
+EXPORT_SYMBOL_GPL(__init_srcu_struct_atomic);
+
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
/**
@@ -305,7 +360,7 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);
int init_srcu_struct_generic(struct srcu_struct *ssp)
{
ssp->srcu_reader_flavor = 0;
- return init_srcu_struct_fields(ssp, false);
+ return init_srcu_struct_fields(ssp, false, false);
}
EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
@@ -322,7 +377,7 @@ EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
int init_srcu_struct_fast(struct srcu_struct *ssp)
{
ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_FAST;
- return init_srcu_struct_fields(ssp, false);
+ return init_srcu_struct_fields(ssp, false, false);
}
EXPORT_SYMBOL_GPL(init_srcu_struct_fast);
@@ -340,10 +395,30 @@ EXPORT_SYMBOL_GPL(init_srcu_struct_fast);
int init_srcu_struct_fast_updown(struct srcu_struct *ssp)
{
ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_FAST_UPDOWN;
- return init_srcu_struct_fields(ssp, false);
+ return init_srcu_struct_fields(ssp, false, false);
}
EXPORT_SYMBOL_GPL(init_srcu_struct_fast_updown);
+/**
+ * init_srcu_struct_atomic - initialize an atomic sleep-RCU structure
+ * @ssp: structure to initialize.
+ *
+ * Use this in place of DEFINE_SRCU_ATOMIC() and DEFINE_STATIC_SRCU_ATOMIC()
+ * for non-static srcu_struct structures that are to be passed to
+ * srcu_read_lock_atomic() and friends. It is necessary to invoke this on a
+ * given srcu_struct before passing that srcu_struct to any other function.
+ * Each srcu_struct represents a separate domain of SRCU protection.
+ *
+ * And yes, we really are defining a sleepable RCU implementation that
+ * cannot sleep. Strange universe we live in, isn't it?
+ */
+int init_srcu_struct_atomic(struct srcu_struct *ssp)
+{
+ ssp->srcu_reader_flavor = SRCU_READ_FLAVOR_ATOMIC;
+ return init_srcu_struct_fields(ssp, false, true);
+}
+EXPORT_SYMBOL_GPL(init_srcu_struct_atomic);
+
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
/*
@@ -432,9 +507,11 @@ static void raw_spin_lock_irqsave_ssp_contention(struct srcu_struct *ssp, unsign
* done with compile-time initialization, so this check is added
* to each update-side SRCU primitive. Use ssp->lock, which -is-
* compile-time initialized, to resolve races involving multiple
- * CPUs trying to garner first-use privileges.
+ * CPUs trying to garner first-use privileges. The is_atomic
+ * parameter tells us that there will never be a reason to
+ * transition to big.
*/
-static void check_init_srcu_struct(struct srcu_struct *ssp)
+static void check_init_srcu_struct(struct srcu_struct *ssp, bool is_atomic)
{
unsigned long flags;
@@ -446,7 +523,7 @@ static void check_init_srcu_struct(struct srcu_struct *ssp)
raw_spin_unlock_irqrestore_rcu_node(ssp->srcu_sup, flags);
return;
}
- init_srcu_struct_fields(ssp, true);
+ init_srcu_struct_fields(ssp, true, is_atomic);
raw_spin_unlock_irqrestore_rcu_node(ssp->srcu_sup, flags);
}
@@ -624,7 +701,7 @@ module_param(srcu_retry_check_delay, ulong, 0444);
#define SRCU_UL_CLAMP_LO(val, low) ((val) > (low) ? (val) : (low))
#define SRCU_UL_CLAMP_HI(val, high) ((val) < (high) ? (val) : (high))
#define SRCU_UL_CLAMP(val, low, high) SRCU_UL_CLAMP_HI(SRCU_UL_CLAMP_LO((val), (low)), (high))
-// per-GP-phase no-delay instances adjusted to allow non-sleeping poll upto
+// per-GP-phase no-delay instances adjusted to allow non-sleeping poll up to
// one jiffies time duration. Mult by 2 is done to factor in the srcu_get_delay()
// called from process_srcu().
#define SRCU_DEFAULT_MAX_NODELAY_PHASE_ADJUSTED \
@@ -688,6 +765,14 @@ void cleanup_srcu_struct(struct srcu_struct *ssp)
unsigned long delay;
struct srcu_usage *sup = ssp->srcu_sup;
+ /*
+ * Drain before the early returns below: they leak the srcu_struct, but
+ * srcu_module_going() frees ->sda regardless, and a staged srcu_data
+ * left chained on a per-CPU list would then dangle. Draining first also
+ * has to precede the ->irq_work sync, since re-issuing a callback can
+ * start a grace period and re-queue ->irq_work, which schedules ->work.
+ */
+ srcu_defer_flush();
raw_spin_lock_irq_rcu_node(ssp->srcu_sup);
delay = srcu_get_delay(ssp);
raw_spin_unlock_irq_rcu_node(ssp->srcu_sup);
@@ -695,7 +780,6 @@ void cleanup_srcu_struct(struct srcu_struct *ssp)
return; /* Just leak it! */
if (WARN_ON(srcu_readers_active(ssp)))
return; /* Just leak it! */
- /* Wait for irq_work to finish first as it may queue a new work. */
irq_work_sync(&sup->irq_work);
flush_delayed_work(&sup->work);
for_each_possible_cpu(cpu) {
@@ -752,7 +836,10 @@ void __srcu_check_read_flavor(struct srcu_struct *ssp, int read_flavor)
WARN_ON_ONCE(ssp->srcu_reader_flavor && read_flavor != ssp->srcu_reader_flavor);
WARN_ON_ONCE(old_read_flavor && ssp->srcu_reader_flavor &&
old_read_flavor != ssp->srcu_reader_flavor);
- WARN_ON_ONCE(read_flavor == SRCU_READ_FLAVOR_FAST && !ssp->srcu_reader_flavor);
+ WARN_ON_ONCE(!!(read_flavor & SRCU_READ_FLAVOR_PREDEF) &&
+ read_flavor != ssp->srcu_reader_flavor);
+ WARN_ON_ONCE(!!(ssp->srcu_reader_flavor & SRCU_READ_FLAVOR_PREDEF) &&
+ read_flavor != ssp->srcu_reader_flavor);
if (!old_read_flavor) {
old_read_flavor = cmpxchg(&sdp->srcu_reader_flavor, 0, read_flavor);
if (!old_read_flavor)
@@ -892,12 +979,13 @@ static void srcu_schedule_cbs_snp(struct srcu_struct *ssp, struct srcu_node *snp
* are initiating callback invocation. This allows the ->srcu_have_cbs[]
* array to have a finite number of elements.
*/
-static void srcu_gp_end(struct srcu_struct *ssp)
+static void srcu_gp_end(struct srcu_struct *ssp, bool is_atomic)
{
unsigned long cbdelay = 1;
bool cbs;
bool last_lvl;
int cpu;
+ unsigned long flags;
unsigned long gpseq;
int idx;
unsigned long mask;
@@ -908,10 +996,11 @@ static void srcu_gp_end(struct srcu_struct *ssp)
struct srcu_usage *sup = ssp->srcu_sup;
/* Prevent more than one additional grace period. */
- mutex_lock(&sup->srcu_cb_mutex);
+ if (!is_atomic)
+ mutex_lock(&sup->srcu_cb_mutex);
/* End the current grace period. */
- raw_spin_lock_irq_rcu_node(sup);
+ raw_spin_lock_irqsave_rcu_node(sup, flags);
idx = rcu_seq_state(sup->srcu_gp_seq);
WARN_ON_ONCE(idx != SRCU_STATE_SCAN2);
if (srcu_gp_is_expedited(ssp))
@@ -922,16 +1011,17 @@ static void srcu_gp_end(struct srcu_struct *ssp)
gpseq = rcu_seq_current(&sup->srcu_gp_seq);
if (ULONG_CMP_LT(sup->srcu_gp_seq_needed_exp, gpseq))
WRITE_ONCE(sup->srcu_gp_seq_needed_exp, gpseq);
- raw_spin_unlock_irq_rcu_node(sup);
- mutex_unlock(&sup->srcu_gp_mutex);
+ raw_spin_unlock_irqrestore_rcu_node(sup, flags);
+ if (!is_atomic)
+ mutex_unlock(&sup->srcu_gp_mutex);
/* A new grace period can start at this point. But only one. */
/* Initiate callback invocation as needed. */
ss_state = smp_load_acquire(&sup->srcu_size_state);
- if (ss_state < SRCU_SIZE_WAIT_BARRIER) {
+ if (!is_atomic && ss_state < SRCU_SIZE_WAIT_BARRIER) {
srcu_schedule_cbs_sdp(per_cpu_ptr(ssp->sda, get_boot_cpu_id()),
cbdelay);
- } else {
+ } else if (!is_atomic) {
idx = rcu_seq_ctr(gpseq) % ARRAY_SIZE(snp->srcu_have_cbs);
srcu_for_each_node_breadth_first(ssp, snp) {
raw_spin_lock_irq_rcu_node(snp);
@@ -959,31 +1049,35 @@ static void srcu_gp_end(struct srcu_struct *ssp)
if (!(gpseq & counter_wrap_check))
for_each_possible_cpu(cpu) {
sdp = per_cpu_ptr(ssp->sda, cpu);
- raw_spin_lock_irq_rcu_node(sdp);
+ raw_spin_lock_irqsave_rcu_node(sdp, flags);
if (ULONG_CMP_GE(gpseq, sdp->srcu_gp_seq_needed + 100))
sdp->srcu_gp_seq_needed = gpseq;
if (ULONG_CMP_GE(gpseq, sdp->srcu_gp_seq_needed_exp + 100))
sdp->srcu_gp_seq_needed_exp = gpseq;
- raw_spin_unlock_irq_rcu_node(sdp);
+ raw_spin_unlock_irqrestore_rcu_node(sdp, flags);
}
/* Callback initiation done, allow grace periods after next. */
- mutex_unlock(&sup->srcu_cb_mutex);
+ if (!is_atomic)
+ mutex_unlock(&sup->srcu_cb_mutex);
/* Start a new grace period if needed. */
- raw_spin_lock_irq_rcu_node(sup);
+ raw_spin_lock_irqsave_rcu_node(sup, flags);
gpseq = rcu_seq_current(&sup->srcu_gp_seq);
if (!rcu_seq_state(gpseq) &&
ULONG_CMP_LT(gpseq, sup->srcu_gp_seq_needed)) {
+ WARN_ON_ONCE(ssp->srcu_reader_flavor & SRCU_READ_FLAVOR_ATOMIC);
srcu_gp_start(ssp);
- raw_spin_unlock_irq_rcu_node(sup);
+ raw_spin_unlock_irqrestore_rcu_node(sup, flags);
srcu_reschedule(ssp, 0);
} else {
- raw_spin_unlock_irq_rcu_node(sup);
+ raw_spin_unlock_irqrestore_rcu_node(sup, flags);
}
- /* Transition to big if needed. */
- if (ss_state != SRCU_SIZE_SMALL && ss_state != SRCU_SIZE_BIG) {
+ /* Transition to big if needed, but never for atomic SRCU. */
+ if (ssp->srcu_reader_flavor == SRCU_READ_FLAVOR_ATOMIC && ss_state != SRCU_SIZE_SMALL) {
+ WARN_ON_ONCE(1);
+ } else if (ss_state != SRCU_SIZE_SMALL && ss_state != SRCU_SIZE_BIG) {
if (ss_state == SRCU_SIZE_ALLOC)
init_srcu_struct_nodes(ssp, GFP_KERNEL);
else
@@ -1098,6 +1192,7 @@ static void srcu_funnel_gp_start(struct srcu_struct *ssp, struct srcu_data *sdp,
/* If grace period not already in progress, start it. */
if (!WARN_ON_ONCE(rcu_seq_done(&sup->srcu_gp_seq, s)) &&
rcu_seq_state(sup->srcu_gp_seq) == SRCU_STATE_IDLE) {
+ WARN_ON_ONCE(ssp->srcu_reader_flavor & SRCU_READ_FLAVOR_ATOMIC);
srcu_gp_start(ssp);
// And how can that list_add() in the "else" clause
@@ -1126,10 +1221,11 @@ static void srcu_funnel_gp_start(struct srcu_struct *ssp, struct srcu_data *sdp,
static bool try_check_zero(struct srcu_struct *ssp, int idx, int trycount)
{
unsigned long curdelay;
+ unsigned long flags;
- raw_spin_lock_irq_rcu_node(ssp->srcu_sup);
+ raw_spin_lock_irqsave_rcu_node(ssp->srcu_sup, flags);
curdelay = !srcu_get_delay(ssp);
- raw_spin_unlock_irq_rcu_node(ssp->srcu_sup);
+ raw_spin_unlock_irqrestore_rcu_node(ssp->srcu_sup, flags);
for (;;) {
if (srcu_readers_active_idx_check(ssp, idx))
@@ -1234,7 +1330,7 @@ static bool srcu_should_expedite(struct srcu_struct *ssp)
unsigned long t;
unsigned long tlast;
- check_init_srcu_struct(ssp);
+ check_init_srcu_struct(ssp, false);
/* If _lite() readers, don't do unsolicited expediting. */
if (this_cpu_read(ssp->sda->srcu_reader_flavor) & SRCU_READ_FLAVOR_SLOWGP)
return false;
@@ -1293,7 +1389,7 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp,
struct srcu_node *sdp_mynode;
int ss_state;
- check_init_srcu_struct(ssp);
+ check_init_srcu_struct(ssp, false);
/*
* While starting a new grace period, make sure we are in an
* SRCU read-side critical section so that the grace-period
@@ -1411,8 +1507,8 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp,
* srcu_read_lock(), and srcu_read_unlock() that are all passed the same
* srcu_struct structure.
*/
-static void __call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
- rcu_callback_t func, bool do_norm)
+static void srcu_do_enqueue(struct srcu_struct *ssp, struct rcu_head *rhp,
+ rcu_callback_t func, bool do_norm)
{
if (debug_rcu_head_queue(rhp)) {
/* Probable double call_srcu(), so leak the callback. */
@@ -1424,6 +1520,110 @@ static void __call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
(void)srcu_gp_start_if_needed(ssp, rhp, do_norm);
}
+/*
+ * The srcu_cblist and srcu_node tree are only accessed with interrupts
+ * disabled, so defer when interrupts are already off rather than enqueue into
+ * an operation that may be in flight on this CPU.
+ */
+static void __call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
+ rcu_callback_t func, bool do_norm)
+{
+ if (WARN_ON_ONCE(ssp->srcu_reader_flavor == SRCU_READ_FLAVOR_ATOMIC))
+ return; // Leak the callback rather than corrupt SRCU state.
+ if (should_rcu_defer()) {
+ struct srcu_defer *sndp = this_cpu_ptr(&srcu_defer);
+ struct srcu_data *sdp;
+
+ /*
+ * Instrumentation on the enqueue path can re-enter here from
+ * inside the drain. Re-queuing would livelock it, so drop the
+ * callback; an NMI cannot loop, so let it through.
+ */
+ if (READ_ONCE(sndp->draining) && !in_nmi()) {
+ WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU),
+ "call_srcu() re-entered during callback drain; leaking callback\n");
+ return;
+ }
+ sdp = this_cpu_ptr(ssp->sda);
+ rhp->func = func;
+ if (llist_add((struct llist_node *)rhp, &sdp->defer_cbs)) {
+ /*
+ * Chain this srcu_data for the drain. ->ssp must be
+ * published here: deferral skips
+ * check_init_srcu_struct(), so on a never-initialized
+ * static srcu_struct the srcu_data are still zeroed and
+ * the drain would read a NULL ->ssp.
+ */
+ sdp->ssp = ssp;
+ if (llist_add(&sdp->defer_link, &sndp->list))
+ irq_work_queue(&sndp->iw);
+ }
+ return;
+ }
+
+ /*
+ * Only reachable from an NMI when deferral is off: before the scheduler
+ * is up, or with CONFIG_RCU_DEFER=n. The enqueue can then race.
+ */
+ WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi());
+
+ srcu_do_enqueue(ssp, rhp, func, do_norm);
+}
+
+/*
+ * Re-issue deferred callbacks straight to srcu_do_enqueue() so they cannot defer
+ * again. ->lock serializes the drainers: the irq_work, srcu_defer_flush() and
+ * srcu_offline_drain().
+ */
+static void __srcu_defer_drain(struct srcu_defer *sndp)
+{
+ struct llist_node *snode, *snext;
+ unsigned long flags;
+
+ if (!IS_ENABLED(CONFIG_RCU_DEFER))
+ return;
+
+ raw_spin_lock_irqsave(&sndp->lock, flags);
+ llist_for_each_safe(snode, snext, llist_del_all(&sndp->list)) {
+ struct srcu_data *sdp = container_of(snode, struct srcu_data, defer_link);
+ struct srcu_struct *ssp = sdp->ssp;
+ struct llist_node *cnode, *cnext;
+
+ cnode = llist_del_all(&sdp->defer_cbs);
+ llist_for_each_safe(cnode, cnext, cnode) {
+ struct rcu_head *rhp = (struct rcu_head *)cnode;
+
+ /* Bounds a node self-linked by a double call_srcu(). */
+ rhp->next = NULL;
+ srcu_do_enqueue(ssp, rhp, rhp->func, true);
+ }
+ }
+ raw_spin_unlock_irqrestore(&sndp->lock, flags);
+}
+
+/*
+ * Only the irq_work drain can be re-fed by its own re-issue, so only it sets
+ * ->draining. A direct drain re-issues onto this CPU, and anything staged
+ * during it is picked up by that CPU's own irq_work.
+ */
+static void srcu_defer_drain(struct irq_work *iw)
+{
+ struct srcu_defer *sndp = container_of(iw, struct srcu_defer, iw);
+
+ WRITE_ONCE(sndp->draining, true);
+ __srcu_defer_drain(sndp);
+ WRITE_ONCE(sndp->draining, false);
+}
+
+/*
+ * Drain @cpu's deferred call_srcu() callbacks once @cpu is dead. One pass
+ * covers every srcu_struct; the re-issue lands on the current CPU.
+ */
+void srcu_offline_drain(int cpu)
+{
+ __srcu_defer_drain(&per_cpu(srcu_defer, cpu));
+}
+
/**
* call_srcu() - Queue a callback for invocation after an SRCU grace period
* @ssp: srcu_struct in queue the callback
@@ -1469,8 +1669,13 @@ static void __synchronize_srcu(struct srcu_struct *ssp, bool do_norm)
if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
return;
+ if (WARN_ON_ONCE(ssp->srcu_reader_flavor == SRCU_READ_FLAVOR_ATOMIC)) {
+ // This works, and exposes a possible bug.
+ synchronize_srcu_atomic(ssp);
+ return;
+ }
might_sleep();
- check_init_srcu_struct(ssp);
+ check_init_srcu_struct(ssp, false);
init_completion(&rcu.completion);
init_rcu_head_on_stack(&rcu.head);
__call_srcu(ssp, &rcu.head, wakeme_after_rcu, do_norm);
@@ -1589,10 +1794,21 @@ EXPORT_SYMBOL_GPL(get_state_synchronize_srcu);
* period has elapsed in the meantime. Unlike get_state_synchronize_srcu(),
* this function also ensures that any needed SRCU grace period will be
* started. This convenience does come at a cost in terms of CPU overhead.
+ *
+ * This function cannot be used with atomic SRCU, which only has
+ * atomic grace periods. Give a warning if someone tries, and return
+ * the same cookie that would have been returned, but refrain from
+ * messing up state by starting a grace period. If someone somewhere
+ * somehow invokes synchronize_srcu_atomic(), passing this cookie to
+ * poll_state_synchronize_srcu() will return true. If no one ever invokes
+ * synchronize_srcu_atomic(), too bad.
*/
unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp)
{
- return srcu_gp_start_if_needed(ssp, NULL, true);
+ if (WARN_ON_ONCE(ssp->srcu_reader_flavor == SRCU_READ_FLAVOR_ATOMIC))
+ return get_state_synchronize_srcu(ssp);
+ else
+ return srcu_gp_start_if_needed(ssp, NULL, true);
}
EXPORT_SYMBOL_GPL(start_poll_synchronize_srcu);
@@ -1678,9 +1894,21 @@ void srcu_barrier(struct srcu_struct *ssp)
{
int cpu;
int idx;
- unsigned long s = rcu_seq_snap(&ssp->srcu_sup->srcu_barrier_seq);
+ unsigned long s;
+
+ check_init_srcu_struct(ssp, false);
+ // Atomic SRCU has no callbacks, so there is nothing to wait on.
+ if (WARN_ON_ONCE(ssp->srcu_reader_flavor == SRCU_READ_FLAVOR_ATOMIC))
+ return;
- check_init_srcu_struct(ssp);
+ /*
+ * Register any deferred callbacks before snapshotting the sequence. The
+ * staging list is per-CPU, not per-srcu_struct, so this also drains
+ * other srcu_structs'.
+ */
+ srcu_defer_flush();
+
+ s = rcu_seq_snap(&ssp->srcu_sup->srcu_barrier_seq);
mutex_lock(&ssp->srcu_sup->srcu_barrier_mutex);
if (rcu_seq_done(&ssp->srcu_sup->srcu_barrier_seq, s)) {
smp_mb(); /* Force ordering following return. */
@@ -1743,6 +1971,9 @@ static void srcu_expedite_current_cb(struct rcu_head *rhp)
* no current grace period, one might be created. If the current grace
* period is currently sleeping, that sleep will complete before expediting
* will take effect.
+ *
+ * This function must not be invoked on srcu_struct structures that are
+ * used with srcu_read_lock_atomic() and synchronize_srcu_atomic().
*/
void srcu_expedite_current(struct srcu_struct *ssp)
{
@@ -1750,6 +1981,9 @@ void srcu_expedite_current(struct srcu_struct *ssp)
bool needcb = false;
struct srcu_data *sdp;
+ // Atomic SRCU has no callbacks, so there is nothing to expedite.
+ if (WARN_ON_ONCE(ssp->srcu_reader_flavor == SRCU_READ_FLAVOR_ATOMIC))
+ return;
migrate_disable();
sdp = this_cpu_ptr(ssp->sda);
raw_spin_lock_irqsave_sdp_contention(sdp, &flags);
@@ -1785,13 +2019,17 @@ EXPORT_SYMBOL_GPL(srcu_batches_completed);
/*
* Core SRCU state machine. Push state bits of ->srcu_gp_seq
* to SRCU_STATE_SCAN2, and invoke srcu_gp_end() when scan has
- * completed in that state.
+ * completed in that state. Set is_atomic to indicate that
+ * the caller is excluding other calls so that ->srcu_gp_mutex
+ * is not needed, and to indicate that blocking is forbidden.
*/
-static void srcu_advance_state(struct srcu_struct *ssp)
+static void srcu_advance_state(struct srcu_struct *ssp, bool is_atomic)
{
+ unsigned long flags;
int idx;
- mutex_lock(&ssp->srcu_sup->srcu_gp_mutex);
+ if (!is_atomic)
+ mutex_lock(&ssp->srcu_sup->srcu_gp_mutex);
/*
* Because readers might be delayed for an extended period after
@@ -1805,19 +2043,23 @@ static void srcu_advance_state(struct srcu_struct *ssp)
*/
idx = rcu_seq_state(smp_load_acquire(&ssp->srcu_sup->srcu_gp_seq)); /* ^^^ */
if (idx == SRCU_STATE_IDLE) {
- raw_spin_lock_irq_rcu_node(ssp->srcu_sup);
+ raw_spin_lock_irqsave_rcu_node(ssp->srcu_sup, flags);
if (ULONG_CMP_GE(ssp->srcu_sup->srcu_gp_seq, ssp->srcu_sup->srcu_gp_seq_needed)) {
WARN_ON_ONCE(rcu_seq_state(ssp->srcu_sup->srcu_gp_seq));
- raw_spin_unlock_irq_rcu_node(ssp->srcu_sup);
- mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
+ raw_spin_unlock_irqrestore_rcu_node(ssp->srcu_sup, flags);
+ if (!is_atomic)
+ mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
return;
}
idx = rcu_seq_state(READ_ONCE(ssp->srcu_sup->srcu_gp_seq));
- if (idx == SRCU_STATE_IDLE)
+ if (idx == SRCU_STATE_IDLE) {
+ WARN_ON_ONCE(ssp->srcu_reader_flavor & SRCU_READ_FLAVOR_ATOMIC);
srcu_gp_start(ssp);
- raw_spin_unlock_irq_rcu_node(ssp->srcu_sup);
+ }
+ raw_spin_unlock_irqrestore_rcu_node(ssp->srcu_sup, flags);
if (idx != SRCU_STATE_IDLE) {
- mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
+ if (!is_atomic)
+ mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
return; /* Someone else started the grace period. */
}
}
@@ -1825,14 +2067,15 @@ static void srcu_advance_state(struct srcu_struct *ssp)
if (rcu_seq_state(READ_ONCE(ssp->srcu_sup->srcu_gp_seq)) == SRCU_STATE_SCAN1) {
idx = !(ssp->srcu_ctrp - &ssp->sda->srcu_ctrs[0]);
if (!try_check_zero(ssp, idx, 1)) {
- mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
+ if (!is_atomic)
+ mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
return; /* readers present, retry later. */
}
srcu_flip(ssp);
- raw_spin_lock_irq_rcu_node(ssp->srcu_sup);
+ raw_spin_lock_irqsave_rcu_node(ssp->srcu_sup, flags);
rcu_seq_set_state(&ssp->srcu_sup->srcu_gp_seq, SRCU_STATE_SCAN2);
ssp->srcu_sup->srcu_n_exp_nodelay = 0;
- raw_spin_unlock_irq_rcu_node(ssp->srcu_sup);
+ raw_spin_unlock_irqrestore_rcu_node(ssp->srcu_sup, flags);
}
if (rcu_seq_state(READ_ONCE(ssp->srcu_sup->srcu_gp_seq)) == SRCU_STATE_SCAN2) {
@@ -1843,13 +2086,145 @@ static void srcu_advance_state(struct srcu_struct *ssp)
*/
idx = !(ssp->srcu_ctrp - &ssp->sda->srcu_ctrs[0]);
if (!try_check_zero(ssp, idx, 2)) {
- mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
+ if (!is_atomic)
+ mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
return; /* readers present, retry later. */
}
ssp->srcu_sup->srcu_n_exp_nodelay = 0;
- srcu_gp_end(ssp); /* Releases ->srcu_gp_mutex. */
+ srcu_gp_end(ssp, is_atomic); /* Releases ->srcu_gp_mutex. */
+ }
+}
+
+/**
+ * synchronize_srcu_atomic - spin for prior SRCU read-side critical-section completion
+ * @ssp: srcu_struct with which to synchronize.
+ *
+ * Similar to synchronize_srcu(), but spins rather than blocking.
+ * Use only with srcu_read_lock_atomic() and srcu_read_unlock_atomic(),
+ * which are forbidden from voluntarily context switching. If
+ * synchronize_srcu_atomic() is invoked from a more restrictive context
+ * (for example, interrupts disabled) for a given srcu_struct structure,
+ * then for that structure, all calls to both srcu_read_lock_atomic()
+ * and srcu_read_unlock_atomic() must be invoked from that same context,
+ * or one that is even more strict.
+ *
+ * If synchronize_srcu_atomic() is invoked on a given srcu_struct
+ * structure, then none of call_srcu(), synchronize_srcu(),
+ * synchronize_srcu_expedited(), start_poll_synchronize_srcu(),
+ * srcu_barrier(), or srcu_expedite_current() may be invoked on that
+ * same structure.
+ *
+ * Because synchronize_srcu_atomic() is even more expedited than is
+ * synchronize_srcu_expedited(), there is no expedited counterpart to
+ * this function.
+ */
+void synchronize_srcu_atomic(struct srcu_struct *ssp)
+{
+ unsigned long srcu_state;
+ struct srcu_usage *sup = ssp->srcu_sup;
+ unsigned long flags;
+ unsigned long rdm0, rdm1;
+ unsigned long unlocks0, unlocks1;
+
+ srcu_lock_sync(&ssp->dep_map);
+
+ RCU_LOCKDEP_WARN(lockdep_is_held(ssp),
+ "Illegal synchronize_srcu_atomic() in same-type SRCU read-side critical section");
+
+ if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE)
+ return;
+
+ // Initialize. Either init_srcu_struct() was invoked or
+ // DEFINE_SRCU() or similar was used. Therefore, no allocation
+ // will be done here.
+ check_init_srcu_struct(ssp, true);
+ srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_ATOMIC);
+
+ // Perhaps others will do our work for us.
+ srcu_state = get_state_synchronize_srcu(ssp);
+ preempt_disable();
+ while (atomic_read(&sup->srcu_atomic_gp_flag) ||
+ atomic_xchg(&sup->srcu_atomic_gp_flag, 1)) {
+ preempt_enable();
+ if (poll_state_synchronize_srcu(ssp, srcu_state))
+ return;
+ cpu_relax();
+ preempt_disable();
}
+
+ // One last check for others doing our work for us under the lock.
+ raw_spin_lock_irqsave_rcu_node(sup, flags);
+ if (poll_state_synchronize_srcu(ssp, srcu_state)) {
+ raw_spin_unlock_irqrestore_rcu_node(sup, flags);
+ atomic_set(&sup->srcu_atomic_gp_flag, 0);
+ preempt_enable();
+ return;
+ }
+
+ // OK, we really have to do it ourselves. Start the grace period.
+ non_block_start(); // We must not voluntarily block!
+ smp_store_release(&sup->srcu_gp_seq_needed, srcu_state); // See srcu_funnel_gp_start().
+ ASSERT_EXCLUSIVE_WRITER(ssp->srcu_sup->srcu_gp_seq);
+ srcu_gp_start(ssp);
+ raw_spin_unlock_irqrestore_rcu_node(sup, flags);
+
+ //
+ // Fastpath: If there are no readers at all, neither grace-period
+ // scan need wait, so both can be satisfied at once without doing
+ // the index flip. The counter-sum proof is the same as that of
+ // srcu_readers_active_idx_check(), but spanning both indices.
+ // Atomic SRCU guarantees that all readers are of
+ // SRCU_READ_FLAVOR_ATOMIC, so the SLOWGP check never triggers and
+ // the ->srcu_reader_flavor masks returned by
+ // srcu_readers_unlock_idx() are unused.
+ //
+ // This proof must follow the grace-period anchor written by the
+ // srcu_gp_start() above, never precede it. With the anchor first,
+ // a reader whose lock increment is missed by the sums below cannot
+ // have incremented its lock counter before the anchor, and therefore
+ // cannot be a pre-existing reader of this grace period. Placing the
+ // proof before the anchor would let this grace period miss a
+ // pre-existing reader and return without waiting for it.
+ //
+ // The smp_mb() pairs with the smp_mb() in __srcu_read_lock()
+ // (store-buffering pattern), which guarantees that a lock is always
+ // counted if the corresponding unlock is counted, the same
+ // memory-ordering guarantee as is provided by
+ // srcu_readers_active_idx_check().
+ //
+ unlocks0 = srcu_readers_unlock_idx(ssp, 0, &rdm0);
+ unlocks1 = srcu_readers_unlock_idx(ssp, 1, &rdm1);
+ smp_mb(); /* A */
+ if (srcu_readers_lock_idx(ssp, 0, false, unlocks0) &&
+ srcu_readers_lock_idx(ssp, 1, false, unlocks1)) {
+ // No readers, so end this grace period manually, skipping
+ // the index flip. Advancing the sequence number via
+ // rcu_seq_start() in srcu_gp_start() above and rcu_seq_end()
+ // below keeps get_state_synchronize_srcu() and
+ // poll_state_synchronize_srcu() working, all under ->lock
+ // and ->srcu_atomic_gp_flag, which excludes concurrent
+ // sequence-number updates.
+ raw_spin_lock_irqsave_rcu_node(sup, flags);
+ rcu_seq_end(&sup->srcu_gp_seq);
+ raw_spin_unlock_irqrestore_rcu_node(sup, flags);
+ WARN_ON_ONCE(!poll_state_synchronize_srcu(ssp, srcu_state));
+ atomic_set_release(&sup->srcu_atomic_gp_flag, 0);
+ preempt_enable();
+ non_block_end();
+ return;
+ }
+
+ // Wait for it to complete, helping it along.
+ while (!poll_state_synchronize_srcu(ssp, srcu_state)) {
+ cpu_relax();
+ srcu_advance_state(ssp, true);
+ }
+ ASSERT_EXCLUSIVE_WRITER(sup->srcu_atomic_gp_flag);
+ atomic_set_release(&sup->srcu_atomic_gp_flag, 0);
+ preempt_enable();
+ non_block_end();
}
+EXPORT_SYMBOL_GPL(synchronize_srcu_atomic);
/*
* Invoke a limited number of SRCU callbacks that have passed through
@@ -1930,6 +2305,7 @@ static void srcu_reschedule(struct srcu_struct *ssp, unsigned long delay)
}
} else if (!rcu_seq_state(ssp->srcu_sup->srcu_gp_seq)) {
/* Outstanding request and no GP. Start one. */
+ WARN_ON_ONCE(ssp->srcu_reader_flavor & SRCU_READ_FLAVOR_ATOMIC);
srcu_gp_start(ssp);
}
raw_spin_unlock_irq_rcu_node(ssp->srcu_sup);
@@ -1951,7 +2327,7 @@ static void process_srcu(struct work_struct *work)
sup = container_of(work, struct srcu_usage, work.work);
ssp = sup->srcu_ssp;
- srcu_advance_state(ssp);
+ srcu_advance_state(ssp, false);
raw_spin_lock_irq_rcu_node(ssp->srcu_sup);
curdelay = srcu_get_delay(ssp);
raw_spin_unlock_irq_rcu_node(ssp->srcu_sup);
@@ -2058,8 +2434,10 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
}
pr_cont(" T(%ld,%ld)\n", s0, s1);
}
- if (SRCU_SIZING_IS_TORTURE())
- srcu_transition_to_big(ssp);
+ if (SRCU_SIZING_IS_TORTURE()) {
+ if (!WARN_ON_ONCE(ssp->srcu_reader_flavor == SRCU_READ_FLAVOR_ATOMIC))
+ srcu_transition_to_big(ssp);
+ }
}
EXPORT_SYMBOL_GPL(srcu_torture_stats_print);
@@ -2135,6 +2513,12 @@ static void srcu_module_going(struct module *mod)
struct srcu_struct *ssp;
struct srcu_struct **sspp = mod->srcu_struct_ptrs;
+ /*
+ * Deferral skips check_init_srcu_struct(), so cleanup_srcu_struct()
+ * below can be skipped for an srcu_struct that has staged callbacks.
+ * Drain them before any ->sda is freed.
+ */
+ srcu_defer_flush();
for (i = 0; i < mod->num_srcu_structs; i++) {
ssp = *(sspp++);
if (!rcu_seq_state(smp_load_acquire(&ssp->srcu_sup->srcu_gp_seq_needed)) &&
diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h
index 627295396cd9..fcac7361ec51 100644
--- a/kernel/rcu/tasks.h
+++ b/kernel/rcu/tasks.h
@@ -178,6 +178,8 @@ static int rcu_task_contend_lim __read_mostly = 100;
module_param(rcu_task_contend_lim, int, 0444);
static int rcu_task_collapse_lim __read_mostly = 10;
module_param(rcu_task_collapse_lim, int, 0444);
+static bool rcu_task_collapse_debug __read_mostly;
+module_param(rcu_task_collapse_debug, bool, 0644);
static int rcu_task_lazy_lim __read_mostly = 32;
module_param(rcu_task_lazy_lim, int, 0444);
@@ -390,7 +392,8 @@ static void call_rcu_tasks_generic(struct rcu_head *rhp, rcu_callback_t func,
WRITE_ONCE(rtp->percpu_enqueue_shift, 0);
WRITE_ONCE(rtp->percpu_dequeue_lim, rcu_task_cpu_ids);
smp_store_release(&rtp->percpu_enqueue_lim, rcu_task_cpu_ids);
- pr_info("Switching %s to per-CPU callback queuing.\n", rtp->name);
+ if (data_race(rcu_task_collapse_debug))
+ pr_info("Switching %s to per-CPU callback queuing.\n", rtp->name);
}
raw_spin_unlock_irqrestore(&rtp->cbs_gbl_lock, flags);
}
@@ -511,7 +514,9 @@ static int rcu_tasks_need_gpcb(struct rcu_tasks *rtp)
smp_store_release(&rtp->percpu_enqueue_lim, 1);
rtp->percpu_dequeue_gpseq = get_state_synchronize_rcu();
gpdone = false;
- pr_info("Starting switch %s to CPU-0 callback queuing.\n", rtp->name);
+ if (data_race(rcu_task_collapse_debug))
+ pr_info("Starting switch %s to CPU-0 callback queuing.\n",
+ rtp->name);
}
raw_spin_unlock_irqrestore(&rtp->cbs_gbl_lock, flags);
}
@@ -519,7 +524,9 @@ static int rcu_tasks_need_gpcb(struct rcu_tasks *rtp)
raw_spin_lock_irqsave(&rtp->cbs_gbl_lock, flags);
if (rtp->percpu_enqueue_lim < rtp->percpu_dequeue_lim) {
WRITE_ONCE(rtp->percpu_dequeue_lim, 1);
- pr_info("Completing switch %s to CPU-0 callback queuing.\n", rtp->name);
+ if (data_race(rcu_task_collapse_debug))
+ pr_info("Completing switch %s to CPU-0 callback queuing.\n",
+ rtp->name);
}
if (rtp->percpu_dequeue_lim == 1) {
for (cpu = rtp->percpu_dequeue_lim; cpu < rcu_task_cpu_ids; cpu++) {
@@ -704,6 +711,8 @@ static void __init rcu_tasks_bootup_oddness(void)
pr_info("\tTasks-RCU CPU stall info multiplier clamped to %d (rcu_task_stall_info_mult).\n", rtsimc);
rcu_task_stall_info_mult = rtsimc;
}
+ if (rcu_task_collapse_debug)
+ pr_info("\tTasks-RCU callback contend/collapse debug enabled.\n");
#endif /* #ifdef CONFIG_TASKS_RCU */
#ifdef CONFIG_TASKS_RCU
pr_info("\tTrampoline variant of Tasks RCU enabled.\n");
diff --git a/kernel/rcu/tiny.c b/kernel/rcu/tiny.c
index dccccd6be941..656b6a682e31 100644
--- a/kernel/rcu/tiny.c
+++ b/kernel/rcu/tiny.c
@@ -11,6 +11,8 @@
*/
#include <linux/completion.h>
#include <linux/interrupt.h>
+#include <linux/irq_work.h>
+#include <linux/llist.h>
#include <linux/notifier.h>
#include <linux/rcupdate_wait.h>
#include <linux/kernel.h>
@@ -42,8 +44,100 @@ static struct rcu_ctrlblk rcu_ctrlblk = {
.gp_seq = 0 - 300UL,
};
+/*
+ * The callback list is only accessed with interrupts disabled, so a call_rcu()
+ * that arrives with interrupts off stages the callback on a lockless list that
+ * an irq_work re-issues later. One global list and irq_work suffice, as Tiny
+ * RCU is uniprocessor.
+ */
+static void rcu_defer_drain(struct irq_work *iw);
+static LLIST_HEAD(rcu_defer_list);
+static struct irq_work rcu_defer_iw = IRQ_WORK_INIT_HARD(rcu_defer_drain);
+static bool rcu_defer_draining;
+
+/*
+ * Also called by __rcu_defer_drain() to re-issue a deferred callback, so it
+ * must not re-check the deferral condition.
+ */
+static void rcu_do_enqueue(struct rcu_head *head, rcu_callback_t func)
+{
+ static atomic_t doublefrees;
+ unsigned long flags;
+
+ if (debug_rcu_head_queue(head)) {
+ if (atomic_inc_return(&doublefrees) < 4) {
+ pr_err("%s(): Double-freed CB %p->%pS()!!! ", __func__, head, head->func);
+ mem_dump_obj(head);
+ }
+ return;
+ }
+
+ head->func = func;
+ head->next = NULL;
+
+ local_irq_save(flags);
+ *rcu_ctrlblk.curtail = head;
+ rcu_ctrlblk.curtail = &head->next;
+ local_irq_restore(flags);
+}
+
+/* Force scheduling for rcu_qs() when enqueuing from the idle task. */
+static void rcu_resched_if_idle(void)
+{
+ if (unlikely(is_idle_task(current)))
+ resched_cpu(0);
+}
+
+static void __rcu_defer_drain(void)
+{
+ struct llist_node *node, *next;
+ bool drained = false;
+ unsigned long flags;
+
+ if (!IS_ENABLED(CONFIG_RCU_DEFER))
+ return;
+
+ /* Re-issued newest-first; nothing depends on call_rcu() ordering. */
+ local_irq_save(flags);
+ llist_for_each_safe(node, next, llist_del_all(&rcu_defer_list)) {
+ struct rcu_head *head = (struct rcu_head *)node;
+
+ /* Bounds a node self-linked by a double call_rcu(). */
+ head->next = NULL;
+ rcu_do_enqueue(head, head->func);
+ drained = true;
+ }
+ local_irq_restore(flags);
+
+ if (drained)
+ rcu_resched_if_idle();
+}
+
+/* Only the irq_work drain can be re-fed by its own re-issue; see Tree RCU. */
+static void rcu_defer_drain(struct irq_work *iw)
+{
+ WRITE_ONCE(rcu_defer_draining, true);
+ __rcu_defer_drain();
+ WRITE_ONCE(rcu_defer_draining, false);
+}
+
+static void call_rcu_defer(struct rcu_head *head, rcu_callback_t func)
+{
+ /* A re-entrant call_rcu() during the drain would livelock it; drop it. */
+ if (READ_ONCE(rcu_defer_draining) && !in_nmi()) {
+ WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU),
+ "call_rcu() re-entered during callback drain; leaking callback\n");
+ return;
+ }
+ head->func = func;
+ if (llist_add((struct llist_node *)head, &rcu_defer_list))
+ irq_work_queue(&rcu_defer_iw);
+}
+
void rcu_barrier(void)
{
+ /* Register any deferred callbacks so the wait below covers them. */
+ __rcu_defer_drain();
wait_rcu_gp(call_rcu_hurry);
}
EXPORT_SYMBOL(rcu_barrier);
@@ -157,29 +251,19 @@ EXPORT_SYMBOL_GPL(synchronize_rcu);
*/
void call_rcu(struct rcu_head *head, rcu_callback_t func)
{
- static atomic_t doublefrees;
- unsigned long flags;
-
- if (debug_rcu_head_queue(head)) {
- if (atomic_inc_return(&doublefrees) < 4) {
- pr_err("%s(): Double-freed CB %p->%pS()!!! ", __func__, head, head->func);
- mem_dump_obj(head);
- }
+ if (should_rcu_defer()) {
+ call_rcu_defer(head, func);
return;
}
- head->func = func;
- head->next = NULL;
+ /*
+ * Only reachable from an NMI when deferral is off: before the scheduler
+ * is up, or with CONFIG_RCU_DEFER=n. The enqueue can then race.
+ */
+ WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi());
- local_irq_save(flags);
- *rcu_ctrlblk.curtail = head;
- rcu_ctrlblk.curtail = &head->next;
- local_irq_restore(flags);
-
- if (unlikely(is_idle_task(current))) {
- /* force scheduling for rcu_qs() */
- resched_cpu(0);
- }
+ rcu_do_enqueue(head, func);
+ rcu_resched_if_idle();
}
EXPORT_SYMBOL_GPL(call_rcu);
@@ -211,10 +295,7 @@ unsigned long start_poll_synchronize_rcu(void)
{
unsigned long gp_seq = get_state_synchronize_rcu();
- if (unlikely(is_idle_task(current))) {
- /* force scheduling for rcu_qs() */
- resched_cpu(0);
- }
+ rcu_resched_if_idle();
return gp_seq;
}
EXPORT_SYMBOL_GPL(start_poll_synchronize_rcu);
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 96848fc1f02b..aea3aef7397e 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -24,6 +24,7 @@
#include <linux/smp.h>
#include <linux/rcupdate_wait.h>
#include <linux/interrupt.h>
+#include <linux/llist.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/nmi.h>
@@ -64,7 +65,6 @@
#include <linux/mm.h>
#include <linux/kasan.h>
#include <linux/context_tracking.h>
-#include "../time/tick-internal.h"
#include "tree.h"
#include "rcu.h"
@@ -3148,21 +3148,19 @@ static void check_cb_ovld(struct rcu_data *rdp)
raw_spin_unlock_rcu_node(rnp);
}
-static void
-__call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy_in)
+/*
+ * Also called by __rcu_defer_drain() to re-issue a deferred callback, so it
+ * must not re-check the deferral condition. Either caller may have interrupts
+ * already disabled, and a drain of a remote CPU re-issues onto the draining
+ * CPU.
+ */
+static void rcu_do_enqueue(struct rcu_head *head, rcu_callback_t func, bool lazy_in)
{
static atomic_t doublefrees;
unsigned long flags;
bool lazy;
struct rcu_data *rdp;
- /* Misaligned rcu_head! */
- WARN_ON_ONCE((unsigned long)head & (sizeof(void *) - 1));
-
- /* Avoid NULL dereference if callback is NULL. */
- if (WARN_ON_ONCE(!func))
- return;
-
if (debug_rcu_head_queue(head)) {
/*
* Probable double call_rcu(), so leak the callback.
@@ -3178,7 +3176,6 @@ __call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy_in)
}
head->func = func;
head->next = NULL;
- kasan_record_aux_stack(head);
local_irq_save(flags);
rdp = this_cpu_ptr(&rcu_data);
@@ -3206,6 +3203,103 @@ __call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy_in)
local_irq_restore(flags);
}
+/*
+ * Re-issue deferred callbacks straight to the enqueue so they cannot defer
+ * again. ->defer_lock serializes the drainers: this CPU's irq_work,
+ * rcu_defer_flush() and rcutree_migrate_callbacks().
+ */
+static void __rcu_defer_drain(struct rcu_data *rdp)
+{
+ struct llist_node *node, *next;
+ unsigned long flags;
+
+ if (!IS_ENABLED(CONFIG_RCU_DEFER))
+ return;
+
+ raw_spin_lock_irqsave(&rdp->defer_lock, flags);
+ llist_for_each_safe(node, next, llist_del_all(&rdp->defer_head)) {
+ struct rcu_head *head = (struct rcu_head *)node;
+
+ /* Bounds a node self-linked by a double call_rcu(). */
+ head->next = NULL;
+ rcu_do_enqueue(head, head->func, false);
+ }
+ raw_spin_unlock_irqrestore(&rdp->defer_lock, flags);
+}
+
+/*
+ * Only the irq_work drain can be re-fed by its own re-issue, so only it sets
+ * ->defer_draining. Anything staged during a direct drain is picked up by the
+ * staging CPU's own irq_work. Every caller of irq_work_run_list() has
+ * interrupts disabled, so the flag is never visible with them enabled.
+ */
+static void rcu_defer_drain(struct irq_work *iw)
+{
+ struct rcu_data *rdp = container_of(iw, struct rcu_data, defer_work);
+
+ WRITE_ONCE(rdp->defer_draining, true);
+ __rcu_defer_drain(rdp);
+ WRITE_ONCE(rdp->defer_draining, false);
+}
+
+/*
+ * Stage @head for this CPU's irq_work to re-issue once interrupts are on. Only
+ * the drain side takes a lock, so this stays safe from NMI.
+ */
+static void call_rcu_defer(struct rcu_head *head, rcu_callback_t func)
+{
+ struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
+
+ /*
+ * Instrumentation on the enqueue path can re-enter here from inside the
+ * drain. Re-queuing would livelock it, so drop the callback; an NMI
+ * cannot loop, so let it through.
+ */
+ if (READ_ONCE(rdp->defer_draining) && !in_nmi()) {
+ WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU),
+ "call_rcu() re-entered during callback drain; leaking callback\n");
+ return;
+ }
+ head->func = func;
+ if (llist_add((struct llist_node *)head, &rdp->defer_head))
+ irq_work_queue(&rdp->defer_work);
+}
+
+static void rcu_defer_flush(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ __rcu_defer_drain(per_cpu_ptr(&rcu_data, cpu));
+}
+
+static void
+__call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy_in)
+{
+ /* Misaligned rcu_head! */
+ WARN_ON_ONCE((unsigned long)head & (sizeof(void *) - 1));
+
+ /* Avoid NULL dereference if callback is NULL. */
+ if (WARN_ON_ONCE(!func))
+ return;
+
+ /* Record the caller: the irq_work's stack says nothing about it. */
+ kasan_record_aux_stack(head);
+
+ if (should_rcu_defer()) {
+ call_rcu_defer(head, func);
+ return;
+ }
+
+ /*
+ * Only reachable from an NMI when deferral is off: before the scheduler
+ * is up, or with CONFIG_RCU_DEFER=n. The enqueue can then race.
+ */
+ WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi());
+
+ rcu_do_enqueue(head, func, lazy_in);
+}
+
#ifdef CONFIG_RCU_LAZY
static bool enable_rcu_lazy __read_mostly = !IS_ENABLED(CONFIG_RCU_LAZY_DEFAULT_OFF);
module_param(enable_rcu_lazy, bool, 0444);
@@ -3896,8 +3990,12 @@ void rcu_barrier(void)
unsigned long flags;
unsigned long gseq;
struct rcu_data *rdp;
- unsigned long s = rcu_seq_snap(&rcu_state.barrier_sequence);
+ unsigned long s;
+ /* Register any deferred callbacks before snapshotting the sequence. */
+ rcu_defer_flush();
+
+ s = rcu_seq_snap(&rcu_state.barrier_sequence);
rcu_barrier_trace(TPS("Begin"), -1, s);
/* Take mutex to serialize concurrent rcu_barrier() requests. */
@@ -3989,12 +4087,12 @@ EXPORT_SYMBOL_GPL(rcu_barrier);
static unsigned long rcu_barrier_last_throttle;
/**
- * rcu_barrier_throttled - Do rcu_barrier(), but limit to one per second
+ * rcu_barrier_throttled - Drain deferred RCU frees, but rate-limit starts
*
- * This can be thought of as guard rails around rcu_barrier() that
- * permits unrestricted userspace use, at least assuming the hardware's
- * try_cmpxchg() is robust. There will be at most one call per second to
- * rcu_barrier() system-wide from use of this function, which means that
+ * This can be thought of as guard rails around the deferred-free barriers
+ * that permit unrestricted userspace use, at least assuming the hardware's
+ * try_cmpxchg() is robust. There will be at most one drain operation started
+ * per sixteenth of a second from use of this function, which means that
* callers might needlessly wait a second or three.
*
* This is intended for use by test suites to avoid OOM by flushing RCU
@@ -4016,14 +4114,24 @@ static void rcu_barrier_throttled(void)
while (time_in_range(j, old, old + HZ / 16) ||
!try_cmpxchg(&rcu_barrier_last_throttle, &old, j)) {
schedule_timeout_idle(HZ / 16);
- if (rcu_seq_done(&rcu_state.barrier_sequence, s)) {
- smp_mb(); /* caller's subsequent code after above check. */
- return;
- }
j = jiffies;
old = READ_ONCE(rcu_barrier_last_throttle);
}
- rcu_barrier();
+ /*
+ * kfree_rcu() can retain objects outside the ordinary callback lists in
+ * per-CPU SLUB sheaves and kvfree_rcu batches. Always drain those queues:
+ * an ordinary barrier does not establish that this work was drained.
+ */
+ kvfree_rcu_barrier();
+ /*
+ * A completed barrier can still cover ordinary callbacks queued before
+ * our entry snapshot. Otherwise, retain an explicit ordinary barrier
+ * without depending on the implementation of kvfree_rcu_barrier().
+ */
+ if (rcu_seq_done(&rcu_state.barrier_sequence, s))
+ smp_mb(); /* caller's subsequent code after above check. */
+ else
+ rcu_barrier();
}
/*
@@ -4231,6 +4339,9 @@ rcu_boot_init_percpu_data(int cpu)
rdp->rcu_onl_gp_state = RCU_GP_CLEANED;
rdp->last_sched_clock = jiffies;
rdp->cpu = cpu;
+ init_llist_head(&rdp->defer_head);
+ raw_spin_lock_init(&rdp->defer_lock);
+ rdp->defer_work = IRQ_WORK_INIT_HARD(rcu_defer_drain);
rcu_boot_init_nocb_percpu_data(rdp);
}
@@ -4528,6 +4639,15 @@ void rcutree_migrate_callbacks(int cpu)
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
bool needwake;
+ /*
+ * Callbacks deferred past the point the outgoing CPU's irq_work can run
+ * sit on ->defer_head, which the ->cblist migration below does not
+ * cover. Drain them here, before the early returns.
+ */
+ __rcu_defer_drain(rdp);
+ /* Likewise for the outgoing CPU's deferred call_srcu() callbacks. */
+ srcu_offline_drain(cpu);
+
if (rcu_rdp_is_offloaded(rdp))
return;
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h
index eedfa43059e8..b7cac7a13b4f 100644
--- a/kernel/rcu/tree.h
+++ b/kernel/rcu/tree.h
@@ -229,6 +229,12 @@ struct rcu_data {
struct rcu_head barrier_head;
int exp_watching_snap; /* Double-check need for IPI. */
+ /* Deferral of an NMI/reentrant call_rcu(); see __call_rcu_common(). */
+ struct llist_head defer_head;
+ struct irq_work defer_work;
+ raw_spinlock_t defer_lock;
+ bool defer_draining;
+
/* 5) Callback offloading. */
#ifdef CONFIG_RCU_NOCB_CPU
struct swait_queue_head nocb_cb_wq; /* For nocb kthreads to sleep on. */
diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h
index 81d42b6e2e31..01bdbd7e28cc 100644
--- a/kernel/rcu/tree_exp.h
+++ b/kernel/rcu/tree_exp.h
@@ -678,7 +678,7 @@ static void synchronize_rcu_expedited_wait(void)
nbcon_cpu_emergency_exit();
- panic_on_rcu_stall(&rcu_exp_stall_cpumask);
+ panic_on_rcu_stall(&rcu_exp_stall_cpumask, NULL, 0);
}
}
diff --git a/kernel/rcu/tree_nocb.h b/kernel/rcu/tree_nocb.h
index 19bb42672baf..b6cfee4e5039 100644
--- a/kernel/rcu/tree_nocb.h
+++ b/kernel/rcu/tree_nocb.h
@@ -1332,9 +1332,8 @@ lazy_rcu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
rcu_nocb_try_flush_bypass(rdp, jiffies);
rcu_nocb_unlock_irqrestore(rdp, flags);
wake_nocb_gp(rdp);
- sc->nr_to_scan -= _count;
count += _count;
- if (sc->nr_to_scan <= 0)
+ if (count >= sc->nr_to_scan)
break;
}
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index 743c16247fc0..ae5d21bae7bc 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -11,8 +11,6 @@
* Paul E. McKenney <paulmck@linux.ibm.com>
*/
-#include "../locking/rtmutex_common.h"
-
static bool rcu_rdp_is_offloaded(struct rcu_data *rdp)
{
/*
diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h
index 091e7850ab6e..803a56af3259 100644
--- a/kernel/rcu/tree_stall.h
+++ b/kernel/rcu/tree_stall.h
@@ -11,6 +11,8 @@
#include <linux/kvm_para.h>
#include <linux/rcu_notifier.h>
#include <linux/smp.h>
+#include <linux/rtmutex.h>
+#include "../locking/rtmutex_common.h"
//////////////////////////////////////////////////////////////////////////////
//
@@ -159,7 +161,8 @@ static int __init check_cpu_stall_init(void)
early_initcall(check_cpu_stall_init);
/* If so specified via sysctl, panic, yielding cleaner stall-warning output. */
-static void panic_on_rcu_stall(const struct cpumask *stalled_mask)
+static void panic_on_rcu_stall(const struct cpumask *stalled_mask,
+ unsigned long *gsp, unsigned long gp_seq)
{
static int cpu_stall;
@@ -170,6 +173,11 @@ static void panic_on_rcu_stall(const struct cpumask *stalled_mask)
if (scx_rcu_cpu_stall(stalled_mask))
return;
+ if (gsp && rcu_seq_current(gsp) != gp_seq) {
+ pr_err("INFO: Stall ended before panic check.\n");
+ return;
+ }
+
if (++cpu_stall < sysctl_max_rcu_stall_to_panic)
return;
@@ -299,6 +307,8 @@ struct rcu_stall_chk_rdr {
int nesting;
union rcu_special rs;
bool on_blkd_list;
+ bool rcu_rdr_running;
+ int rcu_rdr_boosted;
};
/*
@@ -307,6 +317,7 @@ struct rcu_stall_chk_rdr {
*/
static int check_slow_task(struct task_struct *t, void *arg)
{
+ struct rcu_node *rnp;
struct rcu_stall_chk_rdr *rscrp = arg;
if (task_curr(t))
@@ -314,6 +325,19 @@ static int check_slow_task(struct task_struct *t, void *arg)
rscrp->nesting = t->rcu_read_lock_nesting;
rscrp->rs = t->rcu_read_unlock_special;
rscrp->on_blkd_list = !list_empty(&t->rcu_node_entry);
+ rscrp->rcu_rdr_running = task_curr(t);
+ rscrp->rcu_rdr_boosted = 0;
+ if (rscrp->on_blkd_list) {
+ rnp = READ_ONCE(t->rcu_blocked_node);
+ raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
+ if (rnp == READ_ONCE(t->rcu_blocked_node)) {
+ if (rt_mutex_owner(&rnp->boost_mtx.rtmutex) == t)
+ rscrp->rcu_rdr_boosted = 1;
+ } else {
+ rscrp->rcu_rdr_boosted = 2;
+ }
+ raw_spin_unlock_rcu_node(rnp); /* irqs remain disabled. */
+ }
return 0;
}
@@ -351,12 +375,14 @@ static int rcu_print_task_stall(struct rcu_node *rnp, unsigned long flags)
if (task_call_func(t, check_slow_task, &rscr))
pr_cont(" P%d", t->pid);
else
- pr_cont(" P%d/%d:%c%c%c%c",
+ pr_cont(" P%d/%d:%c%c%c%c%c%c",
t->pid, rscr.nesting,
".b"[rscr.rs.b.blocked],
".q"[rscr.rs.b.need_qs],
".e"[rscr.rs.b.exp_hint],
- ".l"[rscr.on_blkd_list]);
+ ".l"[rscr.on_blkd_list],
+ ".R"[rscr.rcu_rdr_running],
+ ".B?"[rscr.rcu_rdr_boosted]);
lockdep_assert_irqs_disabled();
put_task_struct(t);
ndetected++;
@@ -703,7 +729,7 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps)
nbcon_cpu_emergency_exit();
- panic_on_rcu_stall(&rcu_stall_cpumask);
+ panic_on_rcu_stall(&rcu_stall_cpumask, &rcu_state.gp_seq, gp_seq);
rcu_force_quiescent_state(); /* Kick them all. */
}
@@ -758,7 +784,7 @@ static void print_cpu_stall(unsigned long gp_seq, unsigned long gps)
cpumask_clear(&rcu_stall_cpumask);
cpumask_set_cpu(smp_processor_id(), &rcu_stall_cpumask);
- panic_on_rcu_stall(&rcu_stall_cpumask);
+ panic_on_rcu_stall(&rcu_stall_cpumask, &rcu_state.gp_seq, gp_seq);
/*
* Attempt to revive the RCU machinery by forcing a context switch.