Merge branches 'doc.2021.07.20c', 'fixes.2021.08.06a', 'nocb.2021.07.20c', 'nolibc.2021.07.20c', 'tasks.2021.07.20c', 'torture.2021.07.27a' and 'torturescript.2021.07.27a' into HEAD
doc.2021.07.20c: Documentation updates. fixes.2021.08.06a: Miscellaneous fixes. nocb.2021.07.20c: Callback-offloading (NOCB CPU) updates. nolibc.2021.07.20c: Tiny userspace library updates. tasks.2021.07.20c: Tasks RCU updates. torture.2021.07.27a: In-kernel torture-test updates. torturescript.2021.07.27a: Torture-test scripting updates.
This commit is contained in:
@@ -59,7 +59,7 @@ static struct task_struct **writer_tasks;
|
||||
static struct task_struct **reader_tasks;
|
||||
|
||||
static bool lock_is_write_held;
|
||||
static bool lock_is_read_held;
|
||||
static atomic_t lock_is_read_held;
|
||||
static unsigned long last_lock_release;
|
||||
|
||||
struct lock_stress_stats {
|
||||
@@ -682,7 +682,7 @@ static int lock_torture_writer(void *arg)
|
||||
if (WARN_ON_ONCE(lock_is_write_held))
|
||||
lwsp->n_lock_fail++;
|
||||
lock_is_write_held = true;
|
||||
if (WARN_ON_ONCE(lock_is_read_held))
|
||||
if (WARN_ON_ONCE(atomic_read(&lock_is_read_held)))
|
||||
lwsp->n_lock_fail++; /* rare, but... */
|
||||
|
||||
lwsp->n_lock_acquired++;
|
||||
@@ -717,13 +717,13 @@ static int lock_torture_reader(void *arg)
|
||||
schedule_timeout_uninterruptible(1);
|
||||
|
||||
cxt.cur_ops->readlock(tid);
|
||||
lock_is_read_held = true;
|
||||
atomic_inc(&lock_is_read_held);
|
||||
if (WARN_ON_ONCE(lock_is_write_held))
|
||||
lrsp->n_lock_fail++; /* rare, but... */
|
||||
|
||||
lrsp->n_lock_acquired++;
|
||||
cxt.cur_ops->read_delay(&rand);
|
||||
lock_is_read_held = false;
|
||||
atomic_dec(&lock_is_read_held);
|
||||
cxt.cur_ops->readunlock(tid);
|
||||
|
||||
stutter_wait("lock_torture_reader");
|
||||
@@ -738,20 +738,22 @@ static int lock_torture_reader(void *arg)
|
||||
static void __torture_print_stats(char *page,
|
||||
struct lock_stress_stats *statp, bool write)
|
||||
{
|
||||
long cur;
|
||||
bool fail = false;
|
||||
int i, n_stress;
|
||||
long max = 0, min = statp ? statp[0].n_lock_acquired : 0;
|
||||
long max = 0, min = statp ? data_race(statp[0].n_lock_acquired) : 0;
|
||||
long long sum = 0;
|
||||
|
||||
n_stress = write ? cxt.nrealwriters_stress : cxt.nrealreaders_stress;
|
||||
for (i = 0; i < n_stress; i++) {
|
||||
if (statp[i].n_lock_fail)
|
||||
if (data_race(statp[i].n_lock_fail))
|
||||
fail = true;
|
||||
sum += statp[i].n_lock_acquired;
|
||||
if (max < statp[i].n_lock_acquired)
|
||||
max = statp[i].n_lock_acquired;
|
||||
if (min > statp[i].n_lock_acquired)
|
||||
min = statp[i].n_lock_acquired;
|
||||
cur = data_race(statp[i].n_lock_acquired);
|
||||
sum += cur;
|
||||
if (max < cur)
|
||||
max = cur;
|
||||
if (min > cur)
|
||||
min = cur;
|
||||
}
|
||||
page += sprintf(page,
|
||||
"%s: Total: %lld Max/Min: %ld/%ld %s Fail: %d %s\n",
|
||||
@@ -996,7 +998,6 @@ static int __init lock_torture_init(void)
|
||||
}
|
||||
|
||||
if (nreaders_stress) {
|
||||
lock_is_read_held = false;
|
||||
cxt.lrsa = kmalloc_array(cxt.nrealreaders_stress,
|
||||
sizeof(*cxt.lrsa),
|
||||
GFP_KERNEL);
|
||||
|
||||
@@ -487,7 +487,7 @@ retry:
|
||||
if (gp_async) {
|
||||
cur_ops->gp_barrier();
|
||||
}
|
||||
writer_n_durations[me] = i_max;
|
||||
writer_n_durations[me] = i_max + 1;
|
||||
torture_kthread_stopping("rcu_scale_writer");
|
||||
return 0;
|
||||
}
|
||||
@@ -561,7 +561,7 @@ rcu_scale_cleanup(void)
|
||||
wdpp = writer_durations[i];
|
||||
if (!wdpp)
|
||||
continue;
|
||||
for (j = 0; j <= writer_n_durations[i]; j++) {
|
||||
for (j = 0; j < writer_n_durations[i]; j++) {
|
||||
wdp = &wdpp[j];
|
||||
pr_alert("%s%s %4d writer-duration: %5d %llu\n",
|
||||
scale_type, SCALE_FLAG,
|
||||
|
||||
@@ -2022,8 +2022,13 @@ static int rcu_torture_stall(void *args)
|
||||
__func__, raw_smp_processor_id());
|
||||
while (ULONG_CMP_LT((unsigned long)ktime_get_seconds(),
|
||||
stop_at))
|
||||
if (stall_cpu_block)
|
||||
if (stall_cpu_block) {
|
||||
#ifdef CONFIG_PREEMPTION
|
||||
preempt_schedule();
|
||||
#else
|
||||
schedule_timeout_uninterruptible(HZ);
|
||||
#endif
|
||||
}
|
||||
if (stall_cpu_irqsoff)
|
||||
local_irq_enable();
|
||||
else if (!stall_cpu_block)
|
||||
|
||||
+35
-1
@@ -467,6 +467,40 @@ static struct ref_scale_ops acqrel_ops = {
|
||||
.name = "acqrel"
|
||||
};
|
||||
|
||||
static volatile u64 stopopts;
|
||||
|
||||
static void ref_clock_section(const int nloops)
|
||||
{
|
||||
u64 x = 0;
|
||||
int i;
|
||||
|
||||
preempt_disable();
|
||||
for (i = nloops; i >= 0; i--)
|
||||
x += ktime_get_real_fast_ns();
|
||||
preempt_enable();
|
||||
stopopts = x;
|
||||
}
|
||||
|
||||
static void ref_clock_delay_section(const int nloops, const int udl, const int ndl)
|
||||
{
|
||||
u64 x = 0;
|
||||
int i;
|
||||
|
||||
preempt_disable();
|
||||
for (i = nloops; i >= 0; i--) {
|
||||
x += ktime_get_real_fast_ns();
|
||||
un_delay(udl, ndl);
|
||||
}
|
||||
preempt_enable();
|
||||
stopopts = x;
|
||||
}
|
||||
|
||||
static struct ref_scale_ops clock_ops = {
|
||||
.readsection = ref_clock_section,
|
||||
.delaysection = ref_clock_delay_section,
|
||||
.name = "clock"
|
||||
};
|
||||
|
||||
static void rcu_scale_one_reader(void)
|
||||
{
|
||||
if (readdelay <= 0)
|
||||
@@ -759,7 +793,7 @@ ref_scale_init(void)
|
||||
int firsterr = 0;
|
||||
static struct ref_scale_ops *scale_ops[] = {
|
||||
&rcu_ops, &srcu_ops, &rcu_trace_ops, &rcu_tasks_ops, &refcnt_ops, &rwlock_ops,
|
||||
&rwsem_ops, &lock_ops, &lock_irq_ops, &acqrel_ops,
|
||||
&rwsem_ops, &lock_ops, &lock_irq_ops, &acqrel_ops, &clock_ops,
|
||||
};
|
||||
|
||||
if (!torture_init_begin(scale_type, verbose))
|
||||
|
||||
@@ -96,7 +96,7 @@ EXPORT_SYMBOL_GPL(cleanup_srcu_struct);
|
||||
*/
|
||||
void __srcu_read_unlock(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
int newval = ssp->srcu_lock_nesting[idx] - 1;
|
||||
int newval = READ_ONCE(ssp->srcu_lock_nesting[idx]) - 1;
|
||||
|
||||
WRITE_ONCE(ssp->srcu_lock_nesting[idx], newval);
|
||||
if (!newval && READ_ONCE(ssp->srcu_gp_waiting))
|
||||
|
||||
+23
-13
@@ -643,8 +643,8 @@ void exit_tasks_rcu_finish(void) { exit_tasks_rcu_finish_trace(current); }
|
||||
//
|
||||
// "Rude" variant of Tasks RCU, inspired by Steve Rostedt's trick of
|
||||
// passing an empty function to schedule_on_each_cpu(). This approach
|
||||
// provides an asynchronous call_rcu_tasks_rude() API and batching
|
||||
// of concurrent calls to the synchronous synchronize_rcu_rude() API.
|
||||
// provides an asynchronous call_rcu_tasks_rude() API and batching of
|
||||
// concurrent calls to the synchronous synchronize_rcu_tasks_rude() API.
|
||||
// This invokes schedule_on_each_cpu() in order to send IPIs far and wide
|
||||
// and induces otherwise unnecessary context switches on all online CPUs,
|
||||
// whether idle or not.
|
||||
@@ -785,7 +785,10 @@ EXPORT_SYMBOL_GPL(show_rcu_tasks_rude_gp_kthread);
|
||||
// set that task's .need_qs flag so that task's next outermost
|
||||
// rcu_read_unlock_trace() will report the quiescent state (in which
|
||||
// case the count of readers is incremented). If both attempts fail,
|
||||
// the task is added to a "holdout" list.
|
||||
// the task is added to a "holdout" list. Note that IPIs are used
|
||||
// to invoke trc_read_check_handler() in the context of running tasks
|
||||
// in order to avoid ordering overhead on common-case shared-variable
|
||||
// accessses.
|
||||
// rcu_tasks_trace_postscan():
|
||||
// Initialize state and attempt to identify an immediate quiescent
|
||||
// state as above (but only for idle tasks), unblock CPU-hotplug
|
||||
@@ -847,7 +850,7 @@ static DEFINE_IRQ_WORK(rcu_tasks_trace_iw, rcu_read_unlock_iw);
|
||||
/* If we are the last reader, wake up the grace-period kthread. */
|
||||
void rcu_read_unlock_trace_special(struct task_struct *t, int nesting)
|
||||
{
|
||||
int nq = t->trc_reader_special.b.need_qs;
|
||||
int nq = READ_ONCE(t->trc_reader_special.b.need_qs);
|
||||
|
||||
if (IS_ENABLED(CONFIG_TASKS_TRACE_RCU_READ_MB) &&
|
||||
t->trc_reader_special.b.need_mb)
|
||||
@@ -894,7 +897,7 @@ static void trc_read_check_handler(void *t_in)
|
||||
|
||||
// If the task is not in a read-side critical section, and
|
||||
// if this is the last reader, awaken the grace-period kthread.
|
||||
if (likely(!t->trc_reader_nesting)) {
|
||||
if (likely(!READ_ONCE(t->trc_reader_nesting))) {
|
||||
if (WARN_ON_ONCE(atomic_dec_and_test(&trc_n_readers_need_end)))
|
||||
wake_up(&trc_wait);
|
||||
// Mark as checked after decrement to avoid false
|
||||
@@ -903,7 +906,7 @@ static void trc_read_check_handler(void *t_in)
|
||||
goto reset_ipi;
|
||||
}
|
||||
// If we are racing with an rcu_read_unlock_trace(), try again later.
|
||||
if (unlikely(t->trc_reader_nesting < 0)) {
|
||||
if (unlikely(READ_ONCE(t->trc_reader_nesting) < 0)) {
|
||||
if (WARN_ON_ONCE(atomic_dec_and_test(&trc_n_readers_need_end)))
|
||||
wake_up(&trc_wait);
|
||||
goto reset_ipi;
|
||||
@@ -913,14 +916,14 @@ static void trc_read_check_handler(void *t_in)
|
||||
// Get here if the task is in a read-side critical section. Set
|
||||
// its state so that it will awaken the grace-period kthread upon
|
||||
// exit from that critical section.
|
||||
WARN_ON_ONCE(t->trc_reader_special.b.need_qs);
|
||||
WARN_ON_ONCE(READ_ONCE(t->trc_reader_special.b.need_qs));
|
||||
WRITE_ONCE(t->trc_reader_special.b.need_qs, true);
|
||||
|
||||
reset_ipi:
|
||||
// Allow future IPIs to be sent on CPU and for task.
|
||||
// Also order this IPI handler against any later manipulations of
|
||||
// the intended task.
|
||||
smp_store_release(&per_cpu(trc_ipi_to_cpu, smp_processor_id()), false); // ^^^
|
||||
smp_store_release(per_cpu_ptr(&trc_ipi_to_cpu, smp_processor_id()), false); // ^^^
|
||||
smp_store_release(&texp->trc_ipi_to_cpu, -1); // ^^^
|
||||
}
|
||||
|
||||
@@ -950,6 +953,7 @@ static bool trc_inspect_reader(struct task_struct *t, void *arg)
|
||||
n_heavy_reader_ofl_updates++;
|
||||
in_qs = true;
|
||||
} else {
|
||||
// The task is not running, so C-language access is safe.
|
||||
in_qs = likely(!t->trc_reader_nesting);
|
||||
}
|
||||
|
||||
@@ -964,7 +968,7 @@ static bool trc_inspect_reader(struct task_struct *t, void *arg)
|
||||
// state so that it will awaken the grace-period kthread upon exit
|
||||
// from that critical section.
|
||||
atomic_inc(&trc_n_readers_need_end); // One more to wait on.
|
||||
WARN_ON_ONCE(t->trc_reader_special.b.need_qs);
|
||||
WARN_ON_ONCE(READ_ONCE(t->trc_reader_special.b.need_qs));
|
||||
WRITE_ONCE(t->trc_reader_special.b.need_qs, true);
|
||||
return true;
|
||||
}
|
||||
@@ -982,7 +986,7 @@ static void trc_wait_for_one_reader(struct task_struct *t,
|
||||
// The current task had better be in a quiescent state.
|
||||
if (t == current) {
|
||||
t->trc_reader_checked = true;
|
||||
WARN_ON_ONCE(t->trc_reader_nesting);
|
||||
WARN_ON_ONCE(READ_ONCE(t->trc_reader_nesting));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -994,6 +998,12 @@ static void trc_wait_for_one_reader(struct task_struct *t,
|
||||
}
|
||||
put_task_struct(t);
|
||||
|
||||
// If this task is not yet on the holdout list, then we are in
|
||||
// an RCU read-side critical section. Otherwise, the invocation of
|
||||
// rcu_add_holdout() that added it to the list did the necessary
|
||||
// get_task_struct(). Either way, the task cannot be freed out
|
||||
// from under this code.
|
||||
|
||||
// If currently running, send an IPI, either way, add to list.
|
||||
trc_add_holdout(t, bhp);
|
||||
if (task_curr(t) &&
|
||||
@@ -1092,8 +1102,8 @@ static void show_stalled_task_trace(struct task_struct *t, bool *firstreport)
|
||||
".I"[READ_ONCE(t->trc_ipi_to_cpu) > 0],
|
||||
".i"[is_idle_task(t)],
|
||||
".N"[cpu > 0 && tick_nohz_full_cpu(cpu)],
|
||||
t->trc_reader_nesting,
|
||||
" N"[!!t->trc_reader_special.b.need_qs],
|
||||
READ_ONCE(t->trc_reader_nesting),
|
||||
" N"[!!READ_ONCE(t->trc_reader_special.b.need_qs)],
|
||||
cpu);
|
||||
sched_show_task(t);
|
||||
}
|
||||
@@ -1187,7 +1197,7 @@ static void rcu_tasks_trace_postgp(struct rcu_tasks *rtp)
|
||||
static void exit_tasks_rcu_finish_trace(struct task_struct *t)
|
||||
{
|
||||
WRITE_ONCE(t->trc_reader_checked, true);
|
||||
WARN_ON_ONCE(t->trc_reader_nesting);
|
||||
WARN_ON_ONCE(READ_ONCE(t->trc_reader_nesting));
|
||||
WRITE_ONCE(t->trc_reader_nesting, 0);
|
||||
if (WARN_ON_ONCE(READ_ONCE(t->trc_reader_special.b.need_qs)))
|
||||
rcu_read_unlock_trace_special(t, 0);
|
||||
|
||||
+33
-74
@@ -74,17 +74,10 @@
|
||||
|
||||
/* Data structures. */
|
||||
|
||||
/*
|
||||
* Steal a bit from the bottom of ->dynticks for idle entry/exit
|
||||
* control. Initially this is for TLB flushing.
|
||||
*/
|
||||
#define RCU_DYNTICK_CTRL_MASK 0x1
|
||||
#define RCU_DYNTICK_CTRL_CTR (RCU_DYNTICK_CTRL_MASK + 1)
|
||||
|
||||
static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, rcu_data) = {
|
||||
.dynticks_nesting = 1,
|
||||
.dynticks_nmi_nesting = DYNTICK_IRQ_NONIDLE,
|
||||
.dynticks = ATOMIC_INIT(RCU_DYNTICK_CTRL_CTR),
|
||||
.dynticks = ATOMIC_INIT(1),
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
.cblist.flags = SEGCBLIST_SOFTIRQ_ONLY,
|
||||
#endif
|
||||
@@ -258,6 +251,15 @@ void rcu_softirq_qs(void)
|
||||
rcu_tasks_qs(current, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment the current CPU's rcu_data structure's ->dynticks field
|
||||
* with ordering. Return the new value.
|
||||
*/
|
||||
static noinline noinstr unsigned long rcu_dynticks_inc(int incby)
|
||||
{
|
||||
return arch_atomic_add_return(incby, this_cpu_ptr(&rcu_data.dynticks));
|
||||
}
|
||||
|
||||
/*
|
||||
* Record entry into an extended quiescent state. This is only to be
|
||||
* called when not already in an extended quiescent state, that is,
|
||||
@@ -266,7 +268,6 @@ void rcu_softirq_qs(void)
|
||||
*/
|
||||
static noinstr void rcu_dynticks_eqs_enter(void)
|
||||
{
|
||||
struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
|
||||
int seq;
|
||||
|
||||
/*
|
||||
@@ -275,13 +276,9 @@ static noinstr void rcu_dynticks_eqs_enter(void)
|
||||
* next idle sojourn.
|
||||
*/
|
||||
rcu_dynticks_task_trace_enter(); // Before ->dynticks update!
|
||||
seq = arch_atomic_add_return(RCU_DYNTICK_CTRL_CTR, &rdp->dynticks);
|
||||
seq = rcu_dynticks_inc(1);
|
||||
// RCU is no longer watching. Better be in extended quiescent state!
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
|
||||
(seq & RCU_DYNTICK_CTRL_CTR));
|
||||
/* Better not have special action (TLB flush) pending! */
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
|
||||
(seq & RCU_DYNTICK_CTRL_MASK));
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && (seq & 0x1));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -291,7 +288,6 @@ static noinstr void rcu_dynticks_eqs_enter(void)
|
||||
*/
|
||||
static noinstr void rcu_dynticks_eqs_exit(void)
|
||||
{
|
||||
struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
|
||||
int seq;
|
||||
|
||||
/*
|
||||
@@ -299,15 +295,10 @@ static noinstr void rcu_dynticks_eqs_exit(void)
|
||||
* and we also must force ordering with the next RCU read-side
|
||||
* critical section.
|
||||
*/
|
||||
seq = arch_atomic_add_return(RCU_DYNTICK_CTRL_CTR, &rdp->dynticks);
|
||||
seq = rcu_dynticks_inc(1);
|
||||
// RCU is now watching. Better not be in an extended quiescent state!
|
||||
rcu_dynticks_task_trace_exit(); // After ->dynticks update!
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
|
||||
!(seq & RCU_DYNTICK_CTRL_CTR));
|
||||
if (seq & RCU_DYNTICK_CTRL_MASK) {
|
||||
arch_atomic_andnot(RCU_DYNTICK_CTRL_MASK, &rdp->dynticks);
|
||||
smp_mb__after_atomic(); /* _exit after clearing mask. */
|
||||
}
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !(seq & 0x1));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -324,9 +315,9 @@ static void rcu_dynticks_eqs_online(void)
|
||||
{
|
||||
struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
|
||||
|
||||
if (atomic_read(&rdp->dynticks) & RCU_DYNTICK_CTRL_CTR)
|
||||
if (atomic_read(&rdp->dynticks) & 0x1)
|
||||
return;
|
||||
atomic_add(RCU_DYNTICK_CTRL_CTR, &rdp->dynticks);
|
||||
rcu_dynticks_inc(1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -336,9 +327,7 @@ static void rcu_dynticks_eqs_online(void)
|
||||
*/
|
||||
static __always_inline bool rcu_dynticks_curr_cpu_in_eqs(void)
|
||||
{
|
||||
struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
|
||||
|
||||
return !(arch_atomic_read(&rdp->dynticks) & RCU_DYNTICK_CTRL_CTR);
|
||||
return !(atomic_read(this_cpu_ptr(&rcu_data.dynticks)) & 0x1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -347,9 +336,8 @@ static __always_inline bool rcu_dynticks_curr_cpu_in_eqs(void)
|
||||
*/
|
||||
static int rcu_dynticks_snap(struct rcu_data *rdp)
|
||||
{
|
||||
int snap = atomic_add_return(0, &rdp->dynticks);
|
||||
|
||||
return snap & ~RCU_DYNTICK_CTRL_MASK;
|
||||
smp_mb(); // Fundamental RCU ordering guarantee.
|
||||
return atomic_read_acquire(&rdp->dynticks);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -358,7 +346,7 @@ static int rcu_dynticks_snap(struct rcu_data *rdp)
|
||||
*/
|
||||
static bool rcu_dynticks_in_eqs(int snap)
|
||||
{
|
||||
return !(snap & RCU_DYNTICK_CTRL_CTR);
|
||||
return !(snap & 0x1);
|
||||
}
|
||||
|
||||
/* Return true if the specified CPU is currently idle from an RCU viewpoint. */
|
||||
@@ -389,8 +377,7 @@ bool rcu_dynticks_zero_in_eqs(int cpu, int *vp)
|
||||
int snap;
|
||||
|
||||
// If not quiescent, force back to earlier extended quiescent state.
|
||||
snap = atomic_read(&rdp->dynticks) & ~(RCU_DYNTICK_CTRL_MASK |
|
||||
RCU_DYNTICK_CTRL_CTR);
|
||||
snap = atomic_read(&rdp->dynticks) & ~0x1;
|
||||
|
||||
smp_rmb(); // Order ->dynticks and *vp reads.
|
||||
if (READ_ONCE(*vp))
|
||||
@@ -398,32 +385,7 @@ bool rcu_dynticks_zero_in_eqs(int cpu, int *vp)
|
||||
smp_rmb(); // Order *vp read and ->dynticks re-read.
|
||||
|
||||
// If still in the same extended quiescent state, we are good!
|
||||
return snap == (atomic_read(&rdp->dynticks) & ~RCU_DYNTICK_CTRL_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the special (bottom) bit of the specified CPU so that it
|
||||
* will take special action (such as flushing its TLB) on the
|
||||
* next exit from an extended quiescent state. Returns true if
|
||||
* the bit was successfully set, or false if the CPU was not in
|
||||
* an extended quiescent state.
|
||||
*/
|
||||
bool rcu_eqs_special_set(int cpu)
|
||||
{
|
||||
int old;
|
||||
int new;
|
||||
int new_old;
|
||||
struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
|
||||
|
||||
new_old = atomic_read(&rdp->dynticks);
|
||||
do {
|
||||
old = new_old;
|
||||
if (old & RCU_DYNTICK_CTRL_CTR)
|
||||
return false;
|
||||
new = old | RCU_DYNTICK_CTRL_MASK;
|
||||
new_old = atomic_cmpxchg(&rdp->dynticks, old, new);
|
||||
} while (new_old != old);
|
||||
return true;
|
||||
return snap == atomic_read(&rdp->dynticks);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -439,13 +401,12 @@ bool rcu_eqs_special_set(int cpu)
|
||||
*/
|
||||
notrace void rcu_momentary_dyntick_idle(void)
|
||||
{
|
||||
int special;
|
||||
int seq;
|
||||
|
||||
raw_cpu_write(rcu_data.rcu_need_heavy_qs, false);
|
||||
special = atomic_add_return(2 * RCU_DYNTICK_CTRL_CTR,
|
||||
&this_cpu_ptr(&rcu_data)->dynticks);
|
||||
seq = rcu_dynticks_inc(2);
|
||||
/* It is illegal to call this from idle state. */
|
||||
WARN_ON_ONCE(!(special & RCU_DYNTICK_CTRL_CTR));
|
||||
WARN_ON_ONCE(!(seq & 0x1));
|
||||
rcu_preempt_deferred_qs(current);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_momentary_dyntick_idle);
|
||||
@@ -1325,7 +1286,7 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
|
||||
*/
|
||||
jtsq = READ_ONCE(jiffies_to_sched_qs);
|
||||
ruqp = per_cpu_ptr(&rcu_data.rcu_urgent_qs, rdp->cpu);
|
||||
rnhqp = &per_cpu(rcu_data.rcu_need_heavy_qs, rdp->cpu);
|
||||
rnhqp = per_cpu_ptr(&rcu_data.rcu_need_heavy_qs, rdp->cpu);
|
||||
if (!READ_ONCE(*rnhqp) &&
|
||||
(time_after(jiffies, rcu_state.gp_start + jtsq * 2) ||
|
||||
time_after(jiffies, rcu_state.jiffies_resched) ||
|
||||
@@ -1772,7 +1733,7 @@ static void rcu_strict_gp_boundary(void *unused)
|
||||
/*
|
||||
* Initialize a new grace period. Return false if no grace period required.
|
||||
*/
|
||||
static bool rcu_gp_init(void)
|
||||
static noinline_for_stack bool rcu_gp_init(void)
|
||||
{
|
||||
unsigned long firstseq;
|
||||
unsigned long flags;
|
||||
@@ -1966,7 +1927,7 @@ static void rcu_gp_fqs(bool first_time)
|
||||
/*
|
||||
* Loop doing repeated quiescent-state forcing until the grace period ends.
|
||||
*/
|
||||
static void rcu_gp_fqs_loop(void)
|
||||
static noinline_for_stack void rcu_gp_fqs_loop(void)
|
||||
{
|
||||
bool first_gp_fqs;
|
||||
int gf = 0;
|
||||
@@ -1993,8 +1954,8 @@ static void rcu_gp_fqs_loop(void)
|
||||
trace_rcu_grace_period(rcu_state.name, rcu_state.gp_seq,
|
||||
TPS("fqswait"));
|
||||
WRITE_ONCE(rcu_state.gp_state, RCU_GP_WAIT_FQS);
|
||||
ret = swait_event_idle_timeout_exclusive(
|
||||
rcu_state.gp_wq, rcu_gp_fqs_check_wake(&gf), j);
|
||||
(void)swait_event_idle_timeout_exclusive(rcu_state.gp_wq,
|
||||
rcu_gp_fqs_check_wake(&gf), j);
|
||||
rcu_gp_torture_wait();
|
||||
WRITE_ONCE(rcu_state.gp_state, RCU_GP_DOING_FQS);
|
||||
/* Locking provides needed memory barriers. */
|
||||
@@ -2471,9 +2432,6 @@ int rcutree_dead_cpu(unsigned int cpu)
|
||||
WRITE_ONCE(rcu_state.n_online_cpus, rcu_state.n_online_cpus - 1);
|
||||
/* Adjust any no-longer-needed kthreads. */
|
||||
rcu_boost_kthread_setaffinity(rnp, -1);
|
||||
/* Do any needed no-CB deferred wakeups from this CPU. */
|
||||
do_nocb_deferred_wakeup(per_cpu_ptr(&rcu_data, cpu));
|
||||
|
||||
// Stop-machine done, so allow nohz_full to disable tick.
|
||||
tick_dep_clear(TICK_DEP_BIT_RCU);
|
||||
return 0;
|
||||
@@ -4050,7 +4008,7 @@ void rcu_barrier(void)
|
||||
*/
|
||||
init_completion(&rcu_state.barrier_completion);
|
||||
atomic_set(&rcu_state.barrier_cpu_count, 2);
|
||||
get_online_cpus();
|
||||
cpus_read_lock();
|
||||
|
||||
/*
|
||||
* Force each CPU with callbacks to register a new callback.
|
||||
@@ -4081,7 +4039,7 @@ void rcu_barrier(void)
|
||||
rcu_state.barrier_sequence);
|
||||
}
|
||||
}
|
||||
put_online_cpus();
|
||||
cpus_read_unlock();
|
||||
|
||||
/*
|
||||
* Now that we have an rcu_barrier_callback() callback on each
|
||||
@@ -4784,4 +4742,5 @@ void __init rcu_init(void)
|
||||
|
||||
#include "tree_stall.h"
|
||||
#include "tree_exp.h"
|
||||
#include "tree_nocb.h"
|
||||
#include "tree_plugin.h"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+11
-1495
File diff suppressed because it is too large
Load Diff
+69
-42
@@ -7,6 +7,8 @@
|
||||
* Author: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/kvm_para.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Controlling CPU stall warnings, including delay calculation.
|
||||
@@ -117,17 +119,14 @@ static void panic_on_rcu_stall(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* rcu_cpu_stall_reset - prevent further stall warnings in current grace period
|
||||
*
|
||||
* Set the stall-warning timeout way off into the future, thus preventing
|
||||
* any RCU CPU stall-warning messages from appearing in the current set of
|
||||
* RCU grace periods.
|
||||
* rcu_cpu_stall_reset - restart stall-warning timeout for current grace period
|
||||
*
|
||||
* The caller must disable hard irqs.
|
||||
*/
|
||||
void rcu_cpu_stall_reset(void)
|
||||
{
|
||||
WRITE_ONCE(rcu_state.jiffies_stall, jiffies + ULONG_MAX / 2);
|
||||
WRITE_ONCE(rcu_state.jiffies_stall,
|
||||
jiffies + rcu_jiffies_till_stall_check());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -267,8 +266,10 @@ static int rcu_print_task_stall(struct rcu_node *rnp, unsigned long flags)
|
||||
struct task_struct *ts[8];
|
||||
|
||||
lockdep_assert_irqs_disabled();
|
||||
if (!rcu_preempt_blocked_readers_cgp(rnp))
|
||||
if (!rcu_preempt_blocked_readers_cgp(rnp)) {
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
return 0;
|
||||
}
|
||||
pr_err("\tTasks blocked on level-%d rcu_node (CPUs %d-%d):",
|
||||
rnp->level, rnp->grplo, rnp->grphi);
|
||||
t = list_entry(rnp->gp_tasks->prev,
|
||||
@@ -280,8 +281,8 @@ static int rcu_print_task_stall(struct rcu_node *rnp, unsigned long flags)
|
||||
break;
|
||||
}
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
for (i--; i; i--) {
|
||||
t = ts[i];
|
||||
while (i) {
|
||||
t = ts[--i];
|
||||
if (!try_invoke_on_locked_down_task(t, check_slow_task, &rscr))
|
||||
pr_cont(" P%d", t->pid);
|
||||
else
|
||||
@@ -350,7 +351,7 @@ static void rcu_dump_cpu_stacks(void)
|
||||
|
||||
static void print_cpu_stall_fast_no_hz(char *cp, int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
|
||||
sprintf(cp, "last_accelerate: %04lx/%04lx dyntick_enabled: %d",
|
||||
rdp->last_accelerate & 0xffff, jiffies & 0xffff,
|
||||
@@ -464,9 +465,10 @@ static void rcu_check_gp_kthread_starvation(void)
|
||||
pr_err("%s kthread starved for %ld jiffies! g%ld f%#x %s(%d) ->state=%#x ->cpu=%d\n",
|
||||
rcu_state.name, j,
|
||||
(long)rcu_seq_current(&rcu_state.gp_seq),
|
||||
data_race(rcu_state.gp_flags),
|
||||
gp_state_getname(rcu_state.gp_state), rcu_state.gp_state,
|
||||
gpk ? gpk->__state : ~0, cpu);
|
||||
data_race(READ_ONCE(rcu_state.gp_flags)),
|
||||
gp_state_getname(rcu_state.gp_state),
|
||||
data_race(READ_ONCE(rcu_state.gp_state)),
|
||||
gpk ? data_race(READ_ONCE(gpk->__state)) : ~0, cpu);
|
||||
if (gpk) {
|
||||
pr_err("\tUnless %s kthread gets sufficient CPU time, OOM is now expected behavior.\n", rcu_state.name);
|
||||
pr_err("RCU grace-period kthread stack dump:\n");
|
||||
@@ -509,7 +511,7 @@ static void rcu_check_gp_kthread_expired_fqs_timer(void)
|
||||
(long)rcu_seq_current(&rcu_state.gp_seq),
|
||||
data_race(rcu_state.gp_flags),
|
||||
gp_state_getname(RCU_GP_WAIT_FQS), RCU_GP_WAIT_FQS,
|
||||
gpk->__state);
|
||||
data_race(READ_ONCE(gpk->__state)));
|
||||
pr_err("\tPossible timer handling issue on cpu=%d timer-softirq=%u\n",
|
||||
cpu, kstat_softirqs_cpu(TIMER_SOFTIRQ, cpu));
|
||||
}
|
||||
@@ -568,11 +570,11 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps)
|
||||
pr_err("INFO: Stall ended before state dump start\n");
|
||||
} else {
|
||||
j = jiffies;
|
||||
gpa = data_race(rcu_state.gp_activity);
|
||||
gpa = data_race(READ_ONCE(rcu_state.gp_activity));
|
||||
pr_err("All QSes seen, last %s kthread activity %ld (%ld-%ld), jiffies_till_next_fqs=%ld, root ->qsmask %#lx\n",
|
||||
rcu_state.name, j - gpa, j, gpa,
|
||||
data_race(jiffies_till_next_fqs),
|
||||
rcu_get_root()->qsmask);
|
||||
data_race(READ_ONCE(jiffies_till_next_fqs)),
|
||||
data_race(READ_ONCE(rcu_get_root()->qsmask)));
|
||||
}
|
||||
}
|
||||
/* Rewrite if needed in case of slow consoles. */
|
||||
@@ -646,6 +648,7 @@ static void print_cpu_stall(unsigned long gps)
|
||||
|
||||
static void check_cpu_stall(struct rcu_data *rdp)
|
||||
{
|
||||
bool didstall = false;
|
||||
unsigned long gs1;
|
||||
unsigned long gs2;
|
||||
unsigned long gps;
|
||||
@@ -691,24 +694,46 @@ static void check_cpu_stall(struct rcu_data *rdp)
|
||||
ULONG_CMP_GE(gps, js))
|
||||
return; /* No stall or GP completed since entering function. */
|
||||
rnp = rdp->mynode;
|
||||
jn = jiffies + 3 * rcu_jiffies_till_stall_check() + 3;
|
||||
jn = jiffies + ULONG_MAX / 2;
|
||||
if (rcu_gp_in_progress() &&
|
||||
(READ_ONCE(rnp->qsmask) & rdp->grpmask) &&
|
||||
cmpxchg(&rcu_state.jiffies_stall, js, jn) == js) {
|
||||
|
||||
/*
|
||||
* If a virtual machine is stopped by the host it can look to
|
||||
* the watchdog like an RCU stall. Check to see if the host
|
||||
* stopped the vm.
|
||||
*/
|
||||
if (kvm_check_and_clear_guest_paused())
|
||||
return;
|
||||
|
||||
/* We haven't checked in, so go dump stack. */
|
||||
print_cpu_stall(gps);
|
||||
if (READ_ONCE(rcu_cpu_stall_ftrace_dump))
|
||||
rcu_ftrace_dump(DUMP_ALL);
|
||||
didstall = true;
|
||||
|
||||
} else if (rcu_gp_in_progress() &&
|
||||
ULONG_CMP_GE(j, js + RCU_STALL_RAT_DELAY) &&
|
||||
cmpxchg(&rcu_state.jiffies_stall, js, jn) == js) {
|
||||
|
||||
/*
|
||||
* If a virtual machine is stopped by the host it can look to
|
||||
* the watchdog like an RCU stall. Check to see if the host
|
||||
* stopped the vm.
|
||||
*/
|
||||
if (kvm_check_and_clear_guest_paused())
|
||||
return;
|
||||
|
||||
/* They had a few time units to dump stack, so complain. */
|
||||
print_other_cpu_stall(gs2, gps);
|
||||
if (READ_ONCE(rcu_cpu_stall_ftrace_dump))
|
||||
rcu_ftrace_dump(DUMP_ALL);
|
||||
didstall = true;
|
||||
}
|
||||
if (didstall && READ_ONCE(rcu_state.jiffies_stall) == jn) {
|
||||
jn = jiffies + 3 * rcu_jiffies_till_stall_check() + 3;
|
||||
WRITE_ONCE(rcu_state.jiffies_stall, jn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,7 +767,7 @@ bool rcu_check_boost_fail(unsigned long gp_state, int *cpup)
|
||||
|
||||
rcu_for_each_leaf_node(rnp) {
|
||||
if (!cpup) {
|
||||
if (READ_ONCE(rnp->qsmask)) {
|
||||
if (data_race(READ_ONCE(rnp->qsmask))) {
|
||||
return false;
|
||||
} else {
|
||||
if (READ_ONCE(rnp->gp_tasks))
|
||||
@@ -791,32 +816,34 @@ void show_rcu_gp_kthreads(void)
|
||||
struct task_struct *t = READ_ONCE(rcu_state.gp_kthread);
|
||||
|
||||
j = jiffies;
|
||||
ja = j - data_race(rcu_state.gp_activity);
|
||||
jr = j - data_race(rcu_state.gp_req_activity);
|
||||
js = j - data_race(rcu_state.gp_start);
|
||||
jw = j - data_race(rcu_state.gp_wake_time);
|
||||
ja = j - data_race(READ_ONCE(rcu_state.gp_activity));
|
||||
jr = j - data_race(READ_ONCE(rcu_state.gp_req_activity));
|
||||
js = j - data_race(READ_ONCE(rcu_state.gp_start));
|
||||
jw = j - data_race(READ_ONCE(rcu_state.gp_wake_time));
|
||||
pr_info("%s: wait state: %s(%d) ->state: %#x ->rt_priority %u delta ->gp_start %lu ->gp_activity %lu ->gp_req_activity %lu ->gp_wake_time %lu ->gp_wake_seq %ld ->gp_seq %ld ->gp_seq_needed %ld ->gp_max %lu ->gp_flags %#x\n",
|
||||
rcu_state.name, gp_state_getname(rcu_state.gp_state),
|
||||
rcu_state.gp_state, t ? t->__state : 0x1ffff, t ? t->rt_priority : 0xffU,
|
||||
js, ja, jr, jw, (long)data_race(rcu_state.gp_wake_seq),
|
||||
(long)data_race(rcu_state.gp_seq),
|
||||
(long)data_race(rcu_get_root()->gp_seq_needed),
|
||||
data_race(rcu_state.gp_max),
|
||||
data_race(rcu_state.gp_flags));
|
||||
data_race(READ_ONCE(rcu_state.gp_state)),
|
||||
t ? data_race(READ_ONCE(t->__state)) : 0x1ffff, t ? t->rt_priority : 0xffU,
|
||||
js, ja, jr, jw, (long)data_race(READ_ONCE(rcu_state.gp_wake_seq)),
|
||||
(long)data_race(READ_ONCE(rcu_state.gp_seq)),
|
||||
(long)data_race(READ_ONCE(rcu_get_root()->gp_seq_needed)),
|
||||
data_race(READ_ONCE(rcu_state.gp_max)),
|
||||
data_race(READ_ONCE(rcu_state.gp_flags)));
|
||||
rcu_for_each_node_breadth_first(rnp) {
|
||||
if (ULONG_CMP_GE(READ_ONCE(rcu_state.gp_seq), READ_ONCE(rnp->gp_seq_needed)) &&
|
||||
!data_race(rnp->qsmask) && !data_race(rnp->boost_tasks) &&
|
||||
!data_race(rnp->exp_tasks) && !data_race(rnp->gp_tasks))
|
||||
!data_race(READ_ONCE(rnp->qsmask)) && !data_race(READ_ONCE(rnp->boost_tasks)) &&
|
||||
!data_race(READ_ONCE(rnp->exp_tasks)) && !data_race(READ_ONCE(rnp->gp_tasks)))
|
||||
continue;
|
||||
pr_info("\trcu_node %d:%d ->gp_seq %ld ->gp_seq_needed %ld ->qsmask %#lx %c%c%c%c ->n_boosts %ld\n",
|
||||
rnp->grplo, rnp->grphi,
|
||||
(long)data_race(rnp->gp_seq), (long)data_race(rnp->gp_seq_needed),
|
||||
data_race(rnp->qsmask),
|
||||
".b"[!!data_race(rnp->boost_kthread_task)],
|
||||
".B"[!!data_race(rnp->boost_tasks)],
|
||||
".E"[!!data_race(rnp->exp_tasks)],
|
||||
".G"[!!data_race(rnp->gp_tasks)],
|
||||
data_race(rnp->n_boosts));
|
||||
(long)data_race(READ_ONCE(rnp->gp_seq)),
|
||||
(long)data_race(READ_ONCE(rnp->gp_seq_needed)),
|
||||
data_race(READ_ONCE(rnp->qsmask)),
|
||||
".b"[!!data_race(READ_ONCE(rnp->boost_kthread_task))],
|
||||
".B"[!!data_race(READ_ONCE(rnp->boost_tasks))],
|
||||
".E"[!!data_race(READ_ONCE(rnp->exp_tasks))],
|
||||
".G"[!!data_race(READ_ONCE(rnp->gp_tasks))],
|
||||
data_race(READ_ONCE(rnp->n_boosts)));
|
||||
if (!rcu_is_leaf_node(rnp))
|
||||
continue;
|
||||
for_each_leaf_node_possible_cpu(rnp, cpu) {
|
||||
@@ -826,12 +853,12 @@ void show_rcu_gp_kthreads(void)
|
||||
READ_ONCE(rdp->gp_seq_needed)))
|
||||
continue;
|
||||
pr_info("\tcpu %d ->gp_seq_needed %ld\n",
|
||||
cpu, (long)data_race(rdp->gp_seq_needed));
|
||||
cpu, (long)data_race(READ_ONCE(rdp->gp_seq_needed)));
|
||||
}
|
||||
}
|
||||
for_each_possible_cpu(cpu) {
|
||||
rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
cbs += data_race(rdp->n_cbs_invoked);
|
||||
cbs += data_race(READ_ONCE(rdp->n_cbs_invoked));
|
||||
if (rcu_segcblist_is_offloaded(&rdp->cblist))
|
||||
show_rcu_nocb_state(rdp);
|
||||
}
|
||||
@@ -913,11 +940,11 @@ void rcu_fwd_progress_check(unsigned long j)
|
||||
|
||||
if (rcu_gp_in_progress()) {
|
||||
pr_info("%s: GP age %lu jiffies\n",
|
||||
__func__, jiffies - rcu_state.gp_start);
|
||||
__func__, jiffies - data_race(READ_ONCE(rcu_state.gp_start)));
|
||||
show_rcu_gp_kthreads();
|
||||
} else {
|
||||
pr_info("%s: Last GP end %lu jiffies ago\n",
|
||||
__func__, jiffies - rcu_state.gp_end);
|
||||
__func__, jiffies - data_race(READ_ONCE(rcu_state.gp_end)));
|
||||
preempt_disable();
|
||||
rdp = this_cpu_ptr(&rcu_data);
|
||||
rcu_check_gp_start_stall(rdp->mynode, rdp, j);
|
||||
|
||||
+64
-14
@@ -64,6 +64,7 @@ torture_param(bool, use_cpus_read_lock, 0, "Use cpus_read_lock() to exclude CPU
|
||||
torture_param(int, verbose, 0, "Enable verbose debugging printk()s");
|
||||
torture_param(int, weight_resched, -1, "Testing weight for resched_cpu() operations.");
|
||||
torture_param(int, weight_single, -1, "Testing weight for single-CPU no-wait operations.");
|
||||
torture_param(int, weight_single_rpc, -1, "Testing weight for single-CPU RPC operations.");
|
||||
torture_param(int, weight_single_wait, -1, "Testing weight for single-CPU operations.");
|
||||
torture_param(int, weight_many, -1, "Testing weight for multi-CPU no-wait operations.");
|
||||
torture_param(int, weight_many_wait, -1, "Testing weight for multi-CPU operations.");
|
||||
@@ -86,6 +87,8 @@ struct scf_statistics {
|
||||
long long n_resched;
|
||||
long long n_single;
|
||||
long long n_single_ofl;
|
||||
long long n_single_rpc;
|
||||
long long n_single_rpc_ofl;
|
||||
long long n_single_wait;
|
||||
long long n_single_wait_ofl;
|
||||
long long n_many;
|
||||
@@ -101,14 +104,17 @@ static DEFINE_PER_CPU(long long, scf_invoked_count);
|
||||
// Data for random primitive selection
|
||||
#define SCF_PRIM_RESCHED 0
|
||||
#define SCF_PRIM_SINGLE 1
|
||||
#define SCF_PRIM_MANY 2
|
||||
#define SCF_PRIM_ALL 3
|
||||
#define SCF_NPRIMS 7 // Need wait and no-wait versions of each,
|
||||
// except for SCF_PRIM_RESCHED.
|
||||
#define SCF_PRIM_SINGLE_RPC 2
|
||||
#define SCF_PRIM_MANY 3
|
||||
#define SCF_PRIM_ALL 4
|
||||
#define SCF_NPRIMS 8 // Need wait and no-wait versions of each,
|
||||
// except for SCF_PRIM_RESCHED and
|
||||
// SCF_PRIM_SINGLE_RPC.
|
||||
|
||||
static char *scf_prim_name[] = {
|
||||
"resched_cpu",
|
||||
"smp_call_function_single",
|
||||
"smp_call_function_single_rpc",
|
||||
"smp_call_function_many",
|
||||
"smp_call_function",
|
||||
};
|
||||
@@ -128,6 +134,8 @@ struct scf_check {
|
||||
bool scfc_out;
|
||||
int scfc_cpu; // -1 for not _single().
|
||||
bool scfc_wait;
|
||||
bool scfc_rpc;
|
||||
struct completion scfc_completion;
|
||||
};
|
||||
|
||||
// Use to wait for all threads to start.
|
||||
@@ -158,6 +166,7 @@ static void scf_torture_stats_print(void)
|
||||
scfs.n_resched += scf_stats_p[i].n_resched;
|
||||
scfs.n_single += scf_stats_p[i].n_single;
|
||||
scfs.n_single_ofl += scf_stats_p[i].n_single_ofl;
|
||||
scfs.n_single_rpc += scf_stats_p[i].n_single_rpc;
|
||||
scfs.n_single_wait += scf_stats_p[i].n_single_wait;
|
||||
scfs.n_single_wait_ofl += scf_stats_p[i].n_single_wait_ofl;
|
||||
scfs.n_many += scf_stats_p[i].n_many;
|
||||
@@ -168,9 +177,10 @@ static void scf_torture_stats_print(void)
|
||||
if (atomic_read(&n_errs) || atomic_read(&n_mb_in_errs) ||
|
||||
atomic_read(&n_mb_out_errs) || atomic_read(&n_alloc_errs))
|
||||
bangstr = "!!! ";
|
||||
pr_alert("%s %sscf_invoked_count %s: %lld resched: %lld single: %lld/%lld single_ofl: %lld/%lld many: %lld/%lld all: %lld/%lld ",
|
||||
pr_alert("%s %sscf_invoked_count %s: %lld resched: %lld single: %lld/%lld single_ofl: %lld/%lld single_rpc: %lld single_rpc_ofl: %lld many: %lld/%lld all: %lld/%lld ",
|
||||
SCFTORT_FLAG, bangstr, isdone ? "VER" : "ver", invoked_count, scfs.n_resched,
|
||||
scfs.n_single, scfs.n_single_wait, scfs.n_single_ofl, scfs.n_single_wait_ofl,
|
||||
scfs.n_single_rpc, scfs.n_single_rpc_ofl,
|
||||
scfs.n_many, scfs.n_many_wait, scfs.n_all, scfs.n_all_wait);
|
||||
torture_onoff_stats();
|
||||
pr_cont("ste: %d stnmie: %d stnmoe: %d staf: %d\n", atomic_read(&n_errs),
|
||||
@@ -282,10 +292,13 @@ static void scf_handler(void *scfc_in)
|
||||
out:
|
||||
if (unlikely(!scfcp))
|
||||
return;
|
||||
if (scfcp->scfc_wait)
|
||||
if (scfcp->scfc_wait) {
|
||||
WRITE_ONCE(scfcp->scfc_out, true);
|
||||
else
|
||||
if (scfcp->scfc_rpc)
|
||||
complete(&scfcp->scfc_completion);
|
||||
} else {
|
||||
kfree(scfcp);
|
||||
}
|
||||
}
|
||||
|
||||
// As above, but check for correct CPU.
|
||||
@@ -319,6 +332,7 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
|
||||
scfcp->scfc_cpu = -1;
|
||||
scfcp->scfc_wait = scfsp->scfs_wait;
|
||||
scfcp->scfc_out = false;
|
||||
scfcp->scfc_rpc = false;
|
||||
}
|
||||
}
|
||||
switch (scfsp->scfs_prim) {
|
||||
@@ -350,6 +364,34 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
|
||||
scfcp = NULL;
|
||||
}
|
||||
break;
|
||||
case SCF_PRIM_SINGLE_RPC:
|
||||
if (!scfcp)
|
||||
break;
|
||||
cpu = torture_random(trsp) % nr_cpu_ids;
|
||||
scfp->n_single_rpc++;
|
||||
scfcp->scfc_cpu = cpu;
|
||||
scfcp->scfc_wait = true;
|
||||
init_completion(&scfcp->scfc_completion);
|
||||
scfcp->scfc_rpc = true;
|
||||
barrier(); // Prevent race-reduction compiler optimizations.
|
||||
scfcp->scfc_in = true;
|
||||
ret = smp_call_function_single(cpu, scf_handler_1, (void *)scfcp, 0);
|
||||
if (!ret) {
|
||||
if (use_cpus_read_lock)
|
||||
cpus_read_unlock();
|
||||
else
|
||||
preempt_enable();
|
||||
wait_for_completion(&scfcp->scfc_completion);
|
||||
if (use_cpus_read_lock)
|
||||
cpus_read_lock();
|
||||
else
|
||||
preempt_disable();
|
||||
} else {
|
||||
scfp->n_single_rpc_ofl++;
|
||||
kfree(scfcp);
|
||||
scfcp = NULL;
|
||||
}
|
||||
break;
|
||||
case SCF_PRIM_MANY:
|
||||
if (scfsp->scfs_wait)
|
||||
scfp->n_many_wait++;
|
||||
@@ -379,10 +421,12 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
|
||||
}
|
||||
if (scfcp && scfsp->scfs_wait) {
|
||||
if (WARN_ON_ONCE((num_online_cpus() > 1 || scfsp->scfs_prim == SCF_PRIM_SINGLE) &&
|
||||
!scfcp->scfc_out))
|
||||
!scfcp->scfc_out)) {
|
||||
pr_warn("%s: Memory-ordering failure, scfs_prim: %d.\n", __func__, scfsp->scfs_prim);
|
||||
atomic_inc(&n_mb_out_errs); // Leak rather than trash!
|
||||
else
|
||||
} else {
|
||||
kfree(scfcp);
|
||||
}
|
||||
barrier(); // Prevent race-reduction compiler optimizations.
|
||||
}
|
||||
if (use_cpus_read_lock)
|
||||
@@ -453,8 +497,8 @@ static void
|
||||
scftorture_print_module_parms(const char *tag)
|
||||
{
|
||||
pr_alert(SCFTORT_FLAG
|
||||
"--- %s: verbose=%d holdoff=%d longwait=%d nthreads=%d onoff_holdoff=%d onoff_interval=%d shutdown_secs=%d stat_interval=%d stutter=%d use_cpus_read_lock=%d, weight_resched=%d, weight_single=%d, weight_single_wait=%d, weight_many=%d, weight_many_wait=%d, weight_all=%d, weight_all_wait=%d\n", tag,
|
||||
verbose, holdoff, longwait, nthreads, onoff_holdoff, onoff_interval, shutdown, stat_interval, stutter, use_cpus_read_lock, weight_resched, weight_single, weight_single_wait, weight_many, weight_many_wait, weight_all, weight_all_wait);
|
||||
"--- %s: verbose=%d holdoff=%d longwait=%d nthreads=%d onoff_holdoff=%d onoff_interval=%d shutdown_secs=%d stat_interval=%d stutter=%d use_cpus_read_lock=%d, weight_resched=%d, weight_single=%d, weight_single_rpc=%d, weight_single_wait=%d, weight_many=%d, weight_many_wait=%d, weight_all=%d, weight_all_wait=%d\n", tag,
|
||||
verbose, holdoff, longwait, nthreads, onoff_holdoff, onoff_interval, shutdown, stat_interval, stutter, use_cpus_read_lock, weight_resched, weight_single, weight_single_rpc, weight_single_wait, weight_many, weight_many_wait, weight_all, weight_all_wait);
|
||||
}
|
||||
|
||||
static void scf_cleanup_handler(void *unused)
|
||||
@@ -469,7 +513,7 @@ static void scf_torture_cleanup(void)
|
||||
return;
|
||||
|
||||
WRITE_ONCE(scfdone, true);
|
||||
if (nthreads)
|
||||
if (nthreads && scf_stats_p)
|
||||
for (i = 0; i < nthreads; i++)
|
||||
torture_stop_kthread("scftorture_invoker", scf_stats_p[i].task);
|
||||
else
|
||||
@@ -497,6 +541,7 @@ static int __init scf_torture_init(void)
|
||||
int firsterr = 0;
|
||||
unsigned long weight_resched1 = weight_resched;
|
||||
unsigned long weight_single1 = weight_single;
|
||||
unsigned long weight_single_rpc1 = weight_single_rpc;
|
||||
unsigned long weight_single_wait1 = weight_single_wait;
|
||||
unsigned long weight_many1 = weight_many;
|
||||
unsigned long weight_many_wait1 = weight_many_wait;
|
||||
@@ -508,11 +553,13 @@ static int __init scf_torture_init(void)
|
||||
|
||||
scftorture_print_module_parms("Start of test");
|
||||
|
||||
if (weight_resched == -1 && weight_single == -1 && weight_single_wait == -1 &&
|
||||
if (weight_resched == -1 &&
|
||||
weight_single == -1 && weight_single_rpc == -1 && weight_single_wait == -1 &&
|
||||
weight_many == -1 && weight_many_wait == -1 &&
|
||||
weight_all == -1 && weight_all_wait == -1) {
|
||||
weight_resched1 = 2 * nr_cpu_ids;
|
||||
weight_single1 = 2 * nr_cpu_ids;
|
||||
weight_single_rpc1 = 2 * nr_cpu_ids;
|
||||
weight_single_wait1 = 2 * nr_cpu_ids;
|
||||
weight_many1 = 2;
|
||||
weight_many_wait1 = 2;
|
||||
@@ -523,6 +570,8 @@ static int __init scf_torture_init(void)
|
||||
weight_resched1 = 0;
|
||||
if (weight_single == -1)
|
||||
weight_single1 = 0;
|
||||
if (weight_single_rpc == -1)
|
||||
weight_single_rpc1 = 0;
|
||||
if (weight_single_wait == -1)
|
||||
weight_single_wait1 = 0;
|
||||
if (weight_many == -1)
|
||||
@@ -534,7 +583,7 @@ static int __init scf_torture_init(void)
|
||||
if (weight_all_wait == -1)
|
||||
weight_all_wait1 = 0;
|
||||
}
|
||||
if (weight_single1 == 0 && weight_single_wait1 == 0 &&
|
||||
if (weight_single1 == 0 && weight_single_rpc1 == 0 && weight_single_wait1 == 0 &&
|
||||
weight_many1 == 0 && weight_many_wait1 == 0 &&
|
||||
weight_all1 == 0 && weight_all_wait1 == 0) {
|
||||
VERBOSE_SCFTORTOUT_ERRSTRING("all zero weights makes no sense");
|
||||
@@ -546,6 +595,7 @@ static int __init scf_torture_init(void)
|
||||
else if (weight_resched1)
|
||||
VERBOSE_SCFTORTOUT_ERRSTRING("built as module, weight_resched ignored");
|
||||
scf_sel_add(weight_single1, SCF_PRIM_SINGLE, false);
|
||||
scf_sel_add(weight_single_rpc1, SCF_PRIM_SINGLE_RPC, true);
|
||||
scf_sel_add(weight_single_wait1, SCF_PRIM_SINGLE, true);
|
||||
scf_sel_add(weight_many1, SCF_PRIM_MANY, false);
|
||||
scf_sel_add(weight_many_wait1, SCF_PRIM_MANY, true);
|
||||
|
||||
@@ -7781,6 +7781,17 @@ int __sched __cond_resched(void)
|
||||
preempt_schedule_common();
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* In preemptible kernels, ->rcu_read_lock_nesting tells the tick
|
||||
* whether the current CPU is in an RCU read-side critical section,
|
||||
* so the tick can report quiescent states even for CPUs looping
|
||||
* in kernel context. In contrast, in non-preemptible kernels,
|
||||
* RCU readers leave no in-memory hints, which means that CPU-bound
|
||||
* processes executing in kernel context might never report an
|
||||
* RCU quiescent state. Therefore, the following code causes
|
||||
* cond_resched() to report a quiescent state, but only when RCU
|
||||
* is in urgent need of one.
|
||||
*/
|
||||
#ifndef CONFIG_PREEMPT_RCU
|
||||
rcu_all_qs();
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -521,11 +521,11 @@ static void torture_shuffle_tasks(void)
|
||||
struct shuffle_task *stp;
|
||||
|
||||
cpumask_setall(shuffle_tmp_mask);
|
||||
get_online_cpus();
|
||||
cpus_read_lock();
|
||||
|
||||
/* No point in shuffling if there is only one online CPU (ex: UP) */
|
||||
if (num_online_cpus() == 1) {
|
||||
put_online_cpus();
|
||||
cpus_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -541,7 +541,7 @@ static void torture_shuffle_tasks(void)
|
||||
set_cpus_allowed_ptr(stp->st_t, shuffle_tmp_mask);
|
||||
mutex_unlock(&shuffle_task_mutex);
|
||||
|
||||
put_online_cpus();
|
||||
cpus_read_unlock();
|
||||
}
|
||||
|
||||
/* Shuffle tasks across CPUs, with the intent of allowing each CPU in the
|
||||
|
||||
Reference in New Issue
Block a user