mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 04:33:26 +02:00
Merge tag 'rcu.2023.06.22a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu
Pull RCU updates from Paul McKenney: "Documentation updates Miscellaneous fixes, perhaps most notably: - Remove RCU_NONIDLE(). The new visibility of most of the idle loop to RCU has obsoleted this API. - Make the RCU_SOFTIRQ callback-invocation time limit also apply to the rcuc kthreads that invoke callbacks for CONFIG_PREEMPT_RT. - Add a jiffies-based callback-invocation time limit to handle long-running callbacks. (The local_clock() function is only invoked once per 32 callbacks due to its high overhead.) - Stop rcu_tasks_invoke_cbs() from using never-onlined CPUs, which fixes a bug that can occur on systems with non-contiguous CPU numbering. kvfree_rcu updates: - Eliminate the single-argument variant of k[v]free_rcu() now that all uses have been converted to k[v]free_rcu_mightsleep(). - Add WARN_ON_ONCE() checks for k[v]free_rcu*() freeing callbacks too soon. Yes, this is closing the barn door after the horse has escaped, but Murphy says that there will be more horses. Callback-offloading updates: - Fix a number of bugs involving the shrinker and lazy callbacks. Tasks RCU updates Torture-test updates" * tag 'rcu.2023.06.22a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu: (32 commits) torture: Remove duplicated argument -enable-kvm for ppc64 doc/rcutorture: Add description of rcutorture.stall_cpu_block rcu/rcuscale: Stop kfree_scale_thread thread(s) after unloading rcuscale rcu/rcuscale: Move rcu_scale_*() after kfree_scale_cleanup() rcutorture: Correct name of use_softirq module parameter locktorture: Add long_hold to adjust lock-hold delays rcu/nocb: Make shrinker iterate only over NOCB CPUs rcu-tasks: Stop rcu_tasks_invoke_cbs() from using never-onlined CPUs rcu: Make rcu_cpu_starting() rely on interrupts being disabled rcu: Mark rcu_cpu_kthread() accesses to ->rcu_cpu_has_work rcu: Mark additional concurrent load from ->cpu_no_qs.b.exp rcu: Employ jiffies-based backstop to callback time limit rcu: Check callback-invocation time limit for rcuc kthreads rcu: Remove RCU_NONIDLE() rcu: Add more RCU files to kernel-api.rst rcu-tasks: Clarify the cblist_init_generic() function's pr_info() output rcu-tasks: Avoid pr_info() with spin lock in cblist_init_generic() rcu/nocb: Recheck lazy callbacks under the ->nocb_lock from shrinker rcu/nocb: Fix shrinker race against callback enqueuer rcu/nocb: Protect lazy shrinker against concurrent (de-)offloading ...
This commit is contained in:
@@ -33,24 +33,19 @@
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Paul E. McKenney <paulmck@linux.ibm.com>");
|
||||
|
||||
torture_param(int, nwriters_stress, -1,
|
||||
"Number of write-locking stress-test threads");
|
||||
torture_param(int, nreaders_stress, -1,
|
||||
"Number of read-locking stress-test threads");
|
||||
torture_param(int, nwriters_stress, -1, "Number of write-locking stress-test threads");
|
||||
torture_param(int, nreaders_stress, -1, "Number of read-locking stress-test threads");
|
||||
torture_param(int, long_hold, 100, "Do occasional long hold of lock (ms), 0=disable");
|
||||
torture_param(int, onoff_holdoff, 0, "Time after boot before CPU hotplugs (s)");
|
||||
torture_param(int, onoff_interval, 0,
|
||||
"Time between CPU hotplugs (s), 0=disable");
|
||||
torture_param(int, shuffle_interval, 3,
|
||||
"Number of jiffies between shuffles, 0=disable");
|
||||
torture_param(int, onoff_interval, 0, "Time between CPU hotplugs (s), 0=disable");
|
||||
torture_param(int, shuffle_interval, 3, "Number of jiffies between shuffles, 0=disable");
|
||||
torture_param(int, shutdown_secs, 0, "Shutdown time (j), <= zero to disable.");
|
||||
torture_param(int, stat_interval, 60,
|
||||
"Number of seconds between stats printk()s");
|
||||
torture_param(int, stat_interval, 60, "Number of seconds between stats printk()s");
|
||||
torture_param(int, stutter, 5, "Number of jiffies to run/halt test, 0=disable");
|
||||
torture_param(int, rt_boost, 2,
|
||||
"Do periodic rt-boost. 0=Disable, 1=Only for rt_mutex, 2=For all lock types.");
|
||||
"Do periodic rt-boost. 0=Disable, 1=Only for rt_mutex, 2=For all lock types.");
|
||||
torture_param(int, rt_boost_factor, 50, "A factor determining how often rt-boost happens.");
|
||||
torture_param(int, verbose, 1,
|
||||
"Enable verbose debugging printk()s");
|
||||
torture_param(int, verbose, 1, "Enable verbose debugging printk()s");
|
||||
torture_param(int, nested_locks, 0, "Number of nested locks (max = 8)");
|
||||
/* Going much higher trips "BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low!" errors */
|
||||
#define MAX_NESTED_LOCKS 8
|
||||
@@ -120,7 +115,7 @@ static int torture_lock_busted_write_lock(int tid __maybe_unused)
|
||||
|
||||
static void torture_lock_busted_write_delay(struct torture_random_state *trsp)
|
||||
{
|
||||
const unsigned long longdelay_ms = 100;
|
||||
const unsigned long longdelay_ms = long_hold ? long_hold : ULONG_MAX;
|
||||
|
||||
/* We want a long delay occasionally to force massive contention. */
|
||||
if (!(torture_random(trsp) %
|
||||
@@ -198,16 +193,18 @@ __acquires(torture_spinlock)
|
||||
static void torture_spin_lock_write_delay(struct torture_random_state *trsp)
|
||||
{
|
||||
const unsigned long shortdelay_us = 2;
|
||||
const unsigned long longdelay_ms = 100;
|
||||
const unsigned long longdelay_ms = long_hold ? long_hold : ULONG_MAX;
|
||||
unsigned long j;
|
||||
|
||||
/* We want a short delay mostly to emulate likely code, and
|
||||
* we want a long delay occasionally to force massive contention.
|
||||
*/
|
||||
if (!(torture_random(trsp) %
|
||||
(cxt.nrealwriters_stress * 2000 * longdelay_ms)))
|
||||
if (!(torture_random(trsp) % (cxt.nrealwriters_stress * 2000 * longdelay_ms))) {
|
||||
j = jiffies;
|
||||
mdelay(longdelay_ms);
|
||||
if (!(torture_random(trsp) %
|
||||
(cxt.nrealwriters_stress * 2 * shortdelay_us)))
|
||||
pr_alert("%s: delay = %lu jiffies.\n", __func__, jiffies - j);
|
||||
}
|
||||
if (!(torture_random(trsp) % (cxt.nrealwriters_stress * 200 * shortdelay_us)))
|
||||
udelay(shortdelay_us);
|
||||
if (!(torture_random(trsp) % (cxt.nrealwriters_stress * 20000)))
|
||||
torture_preempt_schedule(); /* Allow test to be preempted. */
|
||||
@@ -322,7 +319,7 @@ __acquires(torture_rwlock)
|
||||
static void torture_rwlock_write_delay(struct torture_random_state *trsp)
|
||||
{
|
||||
const unsigned long shortdelay_us = 2;
|
||||
const unsigned long longdelay_ms = 100;
|
||||
const unsigned long longdelay_ms = long_hold ? long_hold : ULONG_MAX;
|
||||
|
||||
/* We want a short delay mostly to emulate likely code, and
|
||||
* we want a long delay occasionally to force massive contention.
|
||||
@@ -455,14 +452,12 @@ __acquires(torture_mutex)
|
||||
|
||||
static void torture_mutex_delay(struct torture_random_state *trsp)
|
||||
{
|
||||
const unsigned long longdelay_ms = 100;
|
||||
const unsigned long longdelay_ms = long_hold ? long_hold : ULONG_MAX;
|
||||
|
||||
/* We want a long delay occasionally to force massive contention. */
|
||||
if (!(torture_random(trsp) %
|
||||
(cxt.nrealwriters_stress * 2000 * longdelay_ms)))
|
||||
mdelay(longdelay_ms * 5);
|
||||
else
|
||||
mdelay(longdelay_ms / 5);
|
||||
if (!(torture_random(trsp) % (cxt.nrealwriters_stress * 20000)))
|
||||
torture_preempt_schedule(); /* Allow test to be preempted. */
|
||||
}
|
||||
@@ -630,7 +625,7 @@ __acquires(torture_rtmutex)
|
||||
static void torture_rtmutex_delay(struct torture_random_state *trsp)
|
||||
{
|
||||
const unsigned long shortdelay_us = 2;
|
||||
const unsigned long longdelay_ms = 100;
|
||||
const unsigned long longdelay_ms = long_hold ? long_hold : ULONG_MAX;
|
||||
|
||||
/*
|
||||
* We want a short delay mostly to emulate likely code, and
|
||||
@@ -640,7 +635,7 @@ static void torture_rtmutex_delay(struct torture_random_state *trsp)
|
||||
(cxt.nrealwriters_stress * 2000 * longdelay_ms)))
|
||||
mdelay(longdelay_ms);
|
||||
if (!(torture_random(trsp) %
|
||||
(cxt.nrealwriters_stress * 2 * shortdelay_us)))
|
||||
(cxt.nrealwriters_stress * 200 * shortdelay_us)))
|
||||
udelay(shortdelay_us);
|
||||
if (!(torture_random(trsp) % (cxt.nrealwriters_stress * 20000)))
|
||||
torture_preempt_schedule(); /* Allow test to be preempted. */
|
||||
@@ -695,14 +690,12 @@ __acquires(torture_rwsem)
|
||||
|
||||
static void torture_rwsem_write_delay(struct torture_random_state *trsp)
|
||||
{
|
||||
const unsigned long longdelay_ms = 100;
|
||||
const unsigned long longdelay_ms = long_hold ? long_hold : ULONG_MAX;
|
||||
|
||||
/* We want a long delay occasionally to force massive contention. */
|
||||
if (!(torture_random(trsp) %
|
||||
(cxt.nrealwriters_stress * 2000 * longdelay_ms)))
|
||||
mdelay(longdelay_ms * 10);
|
||||
else
|
||||
mdelay(longdelay_ms / 10);
|
||||
if (!(torture_random(trsp) % (cxt.nrealwriters_stress * 20000)))
|
||||
torture_preempt_schedule(); /* Allow test to be preempted. */
|
||||
}
|
||||
@@ -848,8 +841,8 @@ static int lock_torture_writer(void *arg)
|
||||
|
||||
lwsp->n_lock_acquired++;
|
||||
}
|
||||
cxt.cur_ops->write_delay(&rand);
|
||||
if (!skip_main_lock) {
|
||||
cxt.cur_ops->write_delay(&rand);
|
||||
lock_is_write_held = false;
|
||||
WRITE_ONCE(last_lock_release, jiffies);
|
||||
cxt.cur_ops->writeunlock(tid);
|
||||
|
@@ -314,4 +314,22 @@ config RCU_LAZY
|
||||
To save power, batch RCU callbacks and flush after delay, memory
|
||||
pressure, or callback list growing too big.
|
||||
|
||||
config RCU_DOUBLE_CHECK_CB_TIME
|
||||
bool "RCU callback-batch backup time check"
|
||||
depends on RCU_EXPERT
|
||||
default n
|
||||
help
|
||||
Use this option to provide more precise enforcement of the
|
||||
rcutree.rcu_resched_ns module parameter in situations where
|
||||
a single RCU callback might run for hundreds of microseconds,
|
||||
thus defeating the 32-callback batching used to amortize the
|
||||
cost of the fine-grained but expensive local_clock() function.
|
||||
|
||||
This option rounds rcutree.rcu_resched_ns up to the next
|
||||
jiffy, and overrides the 32-callback batching if this limit
|
||||
is exceeded.
|
||||
|
||||
Say Y here if you need tighter callback-limit enforcement.
|
||||
Say N here if you are unsure.
|
||||
|
||||
endmenu # "RCU Subsystem"
|
||||
|
@@ -642,4 +642,10 @@ void show_rcu_tasks_trace_gp_kthread(void);
|
||||
static inline void show_rcu_tasks_trace_gp_kthread(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TINY_RCU
|
||||
static inline bool rcu_cpu_beenfullyonline(int cpu) { return true; }
|
||||
#else
|
||||
bool rcu_cpu_beenfullyonline(int cpu);
|
||||
#endif
|
||||
|
||||
#endif /* __LINUX_RCU_H */
|
||||
|
@@ -522,89 +522,6 @@ rcu_scale_print_module_parms(struct rcu_scale_ops *cur_ops, const char *tag)
|
||||
scale_type, tag, nrealreaders, nrealwriters, verbose, shutdown);
|
||||
}
|
||||
|
||||
static void
|
||||
rcu_scale_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int ngps = 0;
|
||||
u64 *wdp;
|
||||
u64 *wdpp;
|
||||
|
||||
/*
|
||||
* Would like warning at start, but everything is expedited
|
||||
* during the mid-boot phase, so have to wait till the end.
|
||||
*/
|
||||
if (rcu_gp_is_expedited() && !rcu_gp_is_normal() && !gp_exp)
|
||||
SCALEOUT_ERRSTRING("All grace periods expedited, no normal ones to measure!");
|
||||
if (rcu_gp_is_normal() && gp_exp)
|
||||
SCALEOUT_ERRSTRING("All grace periods normal, no expedited ones to measure!");
|
||||
if (gp_exp && gp_async)
|
||||
SCALEOUT_ERRSTRING("No expedited async GPs, so went with async!");
|
||||
|
||||
if (torture_cleanup_begin())
|
||||
return;
|
||||
if (!cur_ops) {
|
||||
torture_cleanup_end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reader_tasks) {
|
||||
for (i = 0; i < nrealreaders; i++)
|
||||
torture_stop_kthread(rcu_scale_reader,
|
||||
reader_tasks[i]);
|
||||
kfree(reader_tasks);
|
||||
}
|
||||
|
||||
if (writer_tasks) {
|
||||
for (i = 0; i < nrealwriters; i++) {
|
||||
torture_stop_kthread(rcu_scale_writer,
|
||||
writer_tasks[i]);
|
||||
if (!writer_n_durations)
|
||||
continue;
|
||||
j = writer_n_durations[i];
|
||||
pr_alert("%s%s writer %d gps: %d\n",
|
||||
scale_type, SCALE_FLAG, i, j);
|
||||
ngps += j;
|
||||
}
|
||||
pr_alert("%s%s start: %llu end: %llu duration: %llu gps: %d batches: %ld\n",
|
||||
scale_type, SCALE_FLAG,
|
||||
t_rcu_scale_writer_started, t_rcu_scale_writer_finished,
|
||||
t_rcu_scale_writer_finished -
|
||||
t_rcu_scale_writer_started,
|
||||
ngps,
|
||||
rcuscale_seq_diff(b_rcu_gp_test_finished,
|
||||
b_rcu_gp_test_started));
|
||||
for (i = 0; i < nrealwriters; i++) {
|
||||
if (!writer_durations)
|
||||
break;
|
||||
if (!writer_n_durations)
|
||||
continue;
|
||||
wdpp = writer_durations[i];
|
||||
if (!wdpp)
|
||||
continue;
|
||||
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,
|
||||
i, j, *wdp);
|
||||
if (j % 100 == 0)
|
||||
schedule_timeout_uninterruptible(1);
|
||||
}
|
||||
kfree(writer_durations[i]);
|
||||
}
|
||||
kfree(writer_tasks);
|
||||
kfree(writer_durations);
|
||||
kfree(writer_n_durations);
|
||||
}
|
||||
|
||||
/* Do torture-type-specific cleanup operations. */
|
||||
if (cur_ops->cleanup != NULL)
|
||||
cur_ops->cleanup();
|
||||
|
||||
torture_cleanup_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number if non-negative. If -1, the number of CPUs.
|
||||
* If less than -1, that much less than the number of CPUs, but
|
||||
@@ -624,20 +541,6 @@ static int compute_real(int n)
|
||||
return nr;
|
||||
}
|
||||
|
||||
/*
|
||||
* RCU scalability shutdown kthread. Just waits to be awakened, then shuts
|
||||
* down system.
|
||||
*/
|
||||
static int
|
||||
rcu_scale_shutdown(void *arg)
|
||||
{
|
||||
wait_event_idle(shutdown_wq, atomic_read(&n_rcu_scale_writer_finished) >= nrealwriters);
|
||||
smp_mb(); /* Wake before output. */
|
||||
rcu_scale_cleanup();
|
||||
kernel_power_off();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* kfree_rcu() scalability tests: Start a kfree_rcu() loop on all CPUs for number
|
||||
* of iterations and measure total time and number of GP for all iterations to complete.
|
||||
@@ -874,6 +777,108 @@ unwind:
|
||||
return firsterr;
|
||||
}
|
||||
|
||||
static void
|
||||
rcu_scale_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int ngps = 0;
|
||||
u64 *wdp;
|
||||
u64 *wdpp;
|
||||
|
||||
/*
|
||||
* Would like warning at start, but everything is expedited
|
||||
* during the mid-boot phase, so have to wait till the end.
|
||||
*/
|
||||
if (rcu_gp_is_expedited() && !rcu_gp_is_normal() && !gp_exp)
|
||||
SCALEOUT_ERRSTRING("All grace periods expedited, no normal ones to measure!");
|
||||
if (rcu_gp_is_normal() && gp_exp)
|
||||
SCALEOUT_ERRSTRING("All grace periods normal, no expedited ones to measure!");
|
||||
if (gp_exp && gp_async)
|
||||
SCALEOUT_ERRSTRING("No expedited async GPs, so went with async!");
|
||||
|
||||
if (kfree_rcu_test) {
|
||||
kfree_scale_cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (torture_cleanup_begin())
|
||||
return;
|
||||
if (!cur_ops) {
|
||||
torture_cleanup_end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reader_tasks) {
|
||||
for (i = 0; i < nrealreaders; i++)
|
||||
torture_stop_kthread(rcu_scale_reader,
|
||||
reader_tasks[i]);
|
||||
kfree(reader_tasks);
|
||||
}
|
||||
|
||||
if (writer_tasks) {
|
||||
for (i = 0; i < nrealwriters; i++) {
|
||||
torture_stop_kthread(rcu_scale_writer,
|
||||
writer_tasks[i]);
|
||||
if (!writer_n_durations)
|
||||
continue;
|
||||
j = writer_n_durations[i];
|
||||
pr_alert("%s%s writer %d gps: %d\n",
|
||||
scale_type, SCALE_FLAG, i, j);
|
||||
ngps += j;
|
||||
}
|
||||
pr_alert("%s%s start: %llu end: %llu duration: %llu gps: %d batches: %ld\n",
|
||||
scale_type, SCALE_FLAG,
|
||||
t_rcu_scale_writer_started, t_rcu_scale_writer_finished,
|
||||
t_rcu_scale_writer_finished -
|
||||
t_rcu_scale_writer_started,
|
||||
ngps,
|
||||
rcuscale_seq_diff(b_rcu_gp_test_finished,
|
||||
b_rcu_gp_test_started));
|
||||
for (i = 0; i < nrealwriters; i++) {
|
||||
if (!writer_durations)
|
||||
break;
|
||||
if (!writer_n_durations)
|
||||
continue;
|
||||
wdpp = writer_durations[i];
|
||||
if (!wdpp)
|
||||
continue;
|
||||
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,
|
||||
i, j, *wdp);
|
||||
if (j % 100 == 0)
|
||||
schedule_timeout_uninterruptible(1);
|
||||
}
|
||||
kfree(writer_durations[i]);
|
||||
}
|
||||
kfree(writer_tasks);
|
||||
kfree(writer_durations);
|
||||
kfree(writer_n_durations);
|
||||
}
|
||||
|
||||
/* Do torture-type-specific cleanup operations. */
|
||||
if (cur_ops->cleanup != NULL)
|
||||
cur_ops->cleanup();
|
||||
|
||||
torture_cleanup_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* RCU scalability shutdown kthread. Just waits to be awakened, then shuts
|
||||
* down system.
|
||||
*/
|
||||
static int
|
||||
rcu_scale_shutdown(void *arg)
|
||||
{
|
||||
wait_event_idle(shutdown_wq, atomic_read(&n_rcu_scale_writer_finished) >= nrealwriters);
|
||||
smp_mb(); /* Wake before output. */
|
||||
rcu_scale_cleanup();
|
||||
kernel_power_off();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __init
|
||||
rcu_scale_init(void)
|
||||
{
|
||||
|
@@ -241,7 +241,6 @@ static void cblist_init_generic(struct rcu_tasks *rtp)
|
||||
if (rcu_task_enqueue_lim < 0) {
|
||||
rcu_task_enqueue_lim = 1;
|
||||
rcu_task_cb_adjust = true;
|
||||
pr_info("%s: Setting adjustable number of callback queues.\n", __func__);
|
||||
} else if (rcu_task_enqueue_lim == 0) {
|
||||
rcu_task_enqueue_lim = 1;
|
||||
}
|
||||
@@ -272,7 +271,9 @@ static void cblist_init_generic(struct rcu_tasks *rtp)
|
||||
raw_spin_unlock_rcu_node(rtpcp); // irqs remain disabled.
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&rtp->cbs_gbl_lock, flags);
|
||||
pr_info("%s: Setting shift to %d and lim to %d.\n", __func__, data_race(rtp->percpu_enqueue_shift), data_race(rtp->percpu_enqueue_lim));
|
||||
|
||||
pr_info("%s: Setting shift to %d and lim to %d rcu_task_cb_adjust=%d.\n", rtp->name,
|
||||
data_race(rtp->percpu_enqueue_shift), data_race(rtp->percpu_enqueue_lim), rcu_task_cb_adjust);
|
||||
}
|
||||
|
||||
// IRQ-work handler that does deferred wakeup for call_rcu_tasks_generic().
|
||||
@@ -463,6 +464,7 @@ static void rcu_tasks_invoke_cbs(struct rcu_tasks *rtp, struct rcu_tasks_percpu
|
||||
{
|
||||
int cpu;
|
||||
int cpunext;
|
||||
int cpuwq;
|
||||
unsigned long flags;
|
||||
int len;
|
||||
struct rcu_head *rhp;
|
||||
@@ -473,11 +475,13 @@ static void rcu_tasks_invoke_cbs(struct rcu_tasks *rtp, struct rcu_tasks_percpu
|
||||
cpunext = cpu * 2 + 1;
|
||||
if (cpunext < smp_load_acquire(&rtp->percpu_dequeue_lim)) {
|
||||
rtpcp_next = per_cpu_ptr(rtp->rtpcpu, cpunext);
|
||||
queue_work_on(cpunext, system_wq, &rtpcp_next->rtp_work);
|
||||
cpuwq = rcu_cpu_beenfullyonline(cpunext) ? cpunext : WORK_CPU_UNBOUND;
|
||||
queue_work_on(cpuwq, system_wq, &rtpcp_next->rtp_work);
|
||||
cpunext++;
|
||||
if (cpunext < smp_load_acquire(&rtp->percpu_dequeue_lim)) {
|
||||
rtpcp_next = per_cpu_ptr(rtp->rtpcpu, cpunext);
|
||||
queue_work_on(cpunext, system_wq, &rtpcp_next->rtp_work);
|
||||
cpuwq = rcu_cpu_beenfullyonline(cpunext) ? cpunext : WORK_CPU_UNBOUND;
|
||||
queue_work_on(cpuwq, system_wq, &rtpcp_next->rtp_work);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2046,19 +2046,35 @@ rcu_check_quiescent_state(struct rcu_data *rdp)
|
||||
rcu_report_qs_rdp(rdp);
|
||||
}
|
||||
|
||||
/* Return true if callback-invocation time limit exceeded. */
|
||||
static bool rcu_do_batch_check_time(long count, long tlimit,
|
||||
bool jlimit_check, unsigned long jlimit)
|
||||
{
|
||||
// Invoke local_clock() only once per 32 consecutive callbacks.
|
||||
return unlikely(tlimit) &&
|
||||
(!likely(count & 31) ||
|
||||
(IS_ENABLED(CONFIG_RCU_DOUBLE_CHECK_CB_TIME) &&
|
||||
jlimit_check && time_after(jiffies, jlimit))) &&
|
||||
local_clock() >= tlimit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke any RCU callbacks that have made it to the end of their grace
|
||||
* period. Throttle as specified by rdp->blimit.
|
||||
*/
|
||||
static void rcu_do_batch(struct rcu_data *rdp)
|
||||
{
|
||||
long bl;
|
||||
long count = 0;
|
||||
int div;
|
||||
bool __maybe_unused empty;
|
||||
unsigned long flags;
|
||||
struct rcu_head *rhp;
|
||||
unsigned long jlimit;
|
||||
bool jlimit_check = false;
|
||||
long pending;
|
||||
struct rcu_cblist rcl = RCU_CBLIST_INITIALIZER(rcl);
|
||||
long bl, count = 0;
|
||||
long pending, tlimit = 0;
|
||||
struct rcu_head *rhp;
|
||||
long tlimit = 0;
|
||||
|
||||
/* If no callbacks are ready, just return. */
|
||||
if (!rcu_segcblist_ready_cbs(&rdp->cblist)) {
|
||||
@@ -2082,11 +2098,15 @@ static void rcu_do_batch(struct rcu_data *rdp)
|
||||
div = READ_ONCE(rcu_divisor);
|
||||
div = div < 0 ? 7 : div > sizeof(long) * 8 - 2 ? sizeof(long) * 8 - 2 : div;
|
||||
bl = max(rdp->blimit, pending >> div);
|
||||
if (in_serving_softirq() && unlikely(bl > 100)) {
|
||||
if ((in_serving_softirq() || rdp->rcu_cpu_kthread_status == RCU_KTHREAD_RUNNING) &&
|
||||
(IS_ENABLED(CONFIG_RCU_DOUBLE_CHECK_CB_TIME) || unlikely(bl > 100))) {
|
||||
const long npj = NSEC_PER_SEC / HZ;
|
||||
long rrn = READ_ONCE(rcu_resched_ns);
|
||||
|
||||
rrn = rrn < NSEC_PER_MSEC ? NSEC_PER_MSEC : rrn > NSEC_PER_SEC ? NSEC_PER_SEC : rrn;
|
||||
tlimit = local_clock() + rrn;
|
||||
jlimit = jiffies + (rrn + npj + 1) / npj;
|
||||
jlimit_check = true;
|
||||
}
|
||||
trace_rcu_batch_start(rcu_state.name,
|
||||
rcu_segcblist_n_cbs(&rdp->cblist), bl);
|
||||
@@ -2126,21 +2146,23 @@ static void rcu_do_batch(struct rcu_data *rdp)
|
||||
* Make sure we don't spend too much time here and deprive other
|
||||
* softirq vectors of CPU cycles.
|
||||
*/
|
||||
if (unlikely(tlimit)) {
|
||||
/* only call local_clock() every 32 callbacks */
|
||||
if (likely((count & 31) || local_clock() < tlimit))
|
||||
continue;
|
||||
/* Exceeded the time limit, so leave. */
|
||||
if (rcu_do_batch_check_time(count, tlimit, jlimit_check, jlimit))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// In rcuoc context, so no worries about depriving
|
||||
// other softirq vectors of CPU cycles.
|
||||
// In rcuc/rcuoc context, so no worries about
|
||||
// depriving other softirq vectors of CPU cycles.
|
||||
local_bh_enable();
|
||||
lockdep_assert_irqs_enabled();
|
||||
cond_resched_tasks_rcu_qs();
|
||||
lockdep_assert_irqs_enabled();
|
||||
local_bh_disable();
|
||||
// But rcuc kthreads can delay quiescent-state
|
||||
// reporting, so check time limits for them.
|
||||
if (rdp->rcu_cpu_kthread_status == RCU_KTHREAD_RUNNING &&
|
||||
rcu_do_batch_check_time(count, tlimit, jlimit_check, jlimit)) {
|
||||
rdp->rcu_cpu_has_work = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2459,12 +2481,12 @@ static void rcu_cpu_kthread(unsigned int cpu)
|
||||
*statusp = RCU_KTHREAD_RUNNING;
|
||||
local_irq_disable();
|
||||
work = *workp;
|
||||
*workp = 0;
|
||||
WRITE_ONCE(*workp, 0);
|
||||
local_irq_enable();
|
||||
if (work)
|
||||
rcu_core();
|
||||
local_bh_enable();
|
||||
if (*workp == 0) {
|
||||
if (!READ_ONCE(*workp)) {
|
||||
trace_rcu_utilization(TPS("End CPU kthread@rcu_wait"));
|
||||
*statusp = RCU_KTHREAD_WAITING;
|
||||
return;
|
||||
@@ -2756,7 +2778,7 @@ EXPORT_SYMBOL_GPL(call_rcu);
|
||||
*/
|
||||
struct kvfree_rcu_bulk_data {
|
||||
struct list_head list;
|
||||
unsigned long gp_snap;
|
||||
struct rcu_gp_oldstate gp_snap;
|
||||
unsigned long nr_records;
|
||||
void *records[];
|
||||
};
|
||||
@@ -2773,6 +2795,7 @@ struct kvfree_rcu_bulk_data {
|
||||
* struct kfree_rcu_cpu_work - single batch of kfree_rcu() requests
|
||||
* @rcu_work: Let queue_rcu_work() invoke workqueue handler after grace period
|
||||
* @head_free: List of kfree_rcu() objects waiting for a grace period
|
||||
* @head_free_gp_snap: Grace-period snapshot to check for attempted premature frees.
|
||||
* @bulk_head_free: Bulk-List of kvfree_rcu() objects waiting for a grace period
|
||||
* @krcp: Pointer to @kfree_rcu_cpu structure
|
||||
*/
|
||||
@@ -2780,6 +2803,7 @@ struct kvfree_rcu_bulk_data {
|
||||
struct kfree_rcu_cpu_work {
|
||||
struct rcu_work rcu_work;
|
||||
struct rcu_head *head_free;
|
||||
struct rcu_gp_oldstate head_free_gp_snap;
|
||||
struct list_head bulk_head_free[FREE_N_CHANNELS];
|
||||
struct kfree_rcu_cpu *krcp;
|
||||
};
|
||||
@@ -2900,6 +2924,9 @@ drain_page_cache(struct kfree_rcu_cpu *krcp)
|
||||
struct llist_node *page_list, *pos, *n;
|
||||
int freed = 0;
|
||||
|
||||
if (!rcu_min_cached_objs)
|
||||
return 0;
|
||||
|
||||
raw_spin_lock_irqsave(&krcp->lock, flags);
|
||||
page_list = llist_del_all(&krcp->bkvcache);
|
||||
WRITE_ONCE(krcp->nr_bkv_objs, 0);
|
||||
@@ -2920,24 +2947,25 @@ kvfree_rcu_bulk(struct kfree_rcu_cpu *krcp,
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
debug_rcu_bhead_unqueue(bnode);
|
||||
if (!WARN_ON_ONCE(!poll_state_synchronize_rcu_full(&bnode->gp_snap))) {
|
||||
debug_rcu_bhead_unqueue(bnode);
|
||||
rcu_lock_acquire(&rcu_callback_map);
|
||||
if (idx == 0) { // kmalloc() / kfree().
|
||||
trace_rcu_invoke_kfree_bulk_callback(
|
||||
rcu_state.name, bnode->nr_records,
|
||||
bnode->records);
|
||||
|
||||
rcu_lock_acquire(&rcu_callback_map);
|
||||
if (idx == 0) { // kmalloc() / kfree().
|
||||
trace_rcu_invoke_kfree_bulk_callback(
|
||||
rcu_state.name, bnode->nr_records,
|
||||
bnode->records);
|
||||
kfree_bulk(bnode->nr_records, bnode->records);
|
||||
} else { // vmalloc() / vfree().
|
||||
for (i = 0; i < bnode->nr_records; i++) {
|
||||
trace_rcu_invoke_kvfree_callback(
|
||||
rcu_state.name, bnode->records[i], 0);
|
||||
|
||||
kfree_bulk(bnode->nr_records, bnode->records);
|
||||
} else { // vmalloc() / vfree().
|
||||
for (i = 0; i < bnode->nr_records; i++) {
|
||||
trace_rcu_invoke_kvfree_callback(
|
||||
rcu_state.name, bnode->records[i], 0);
|
||||
|
||||
vfree(bnode->records[i]);
|
||||
vfree(bnode->records[i]);
|
||||
}
|
||||
}
|
||||
rcu_lock_release(&rcu_callback_map);
|
||||
}
|
||||
rcu_lock_release(&rcu_callback_map);
|
||||
|
||||
raw_spin_lock_irqsave(&krcp->lock, flags);
|
||||
if (put_cached_bnode(krcp, bnode))
|
||||
@@ -2984,6 +3012,7 @@ static void kfree_rcu_work(struct work_struct *work)
|
||||
struct rcu_head *head;
|
||||
struct kfree_rcu_cpu *krcp;
|
||||
struct kfree_rcu_cpu_work *krwp;
|
||||
struct rcu_gp_oldstate head_gp_snap;
|
||||
int i;
|
||||
|
||||
krwp = container_of(to_rcu_work(work),
|
||||
@@ -2998,6 +3027,7 @@ static void kfree_rcu_work(struct work_struct *work)
|
||||
// Channel 3.
|
||||
head = krwp->head_free;
|
||||
krwp->head_free = NULL;
|
||||
head_gp_snap = krwp->head_free_gp_snap;
|
||||
raw_spin_unlock_irqrestore(&krcp->lock, flags);
|
||||
|
||||
// Handle the first two channels.
|
||||
@@ -3014,7 +3044,8 @@ static void kfree_rcu_work(struct work_struct *work)
|
||||
* queued on a linked list through their rcu_head structures.
|
||||
* This list is named "Channel 3".
|
||||
*/
|
||||
kvfree_rcu_list(head);
|
||||
if (head && !WARN_ON_ONCE(!poll_state_synchronize_rcu_full(&head_gp_snap)))
|
||||
kvfree_rcu_list(head);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -3081,7 +3112,7 @@ kvfree_rcu_drain_ready(struct kfree_rcu_cpu *krcp)
|
||||
INIT_LIST_HEAD(&bulk_ready[i]);
|
||||
|
||||
list_for_each_entry_safe_reverse(bnode, n, &krcp->bulk_head[i], list) {
|
||||
if (!poll_state_synchronize_rcu(bnode->gp_snap))
|
||||
if (!poll_state_synchronize_rcu_full(&bnode->gp_snap))
|
||||
break;
|
||||
|
||||
atomic_sub(bnode->nr_records, &krcp->bulk_count[i]);
|
||||
@@ -3146,6 +3177,7 @@ static void kfree_rcu_monitor(struct work_struct *work)
|
||||
// objects queued on the linked list.
|
||||
if (!krwp->head_free) {
|
||||
krwp->head_free = krcp->head;
|
||||
get_state_synchronize_rcu_full(&krwp->head_free_gp_snap);
|
||||
atomic_set(&krcp->head_count, 0);
|
||||
WRITE_ONCE(krcp->head, NULL);
|
||||
}
|
||||
@@ -3194,7 +3226,7 @@ static void fill_page_cache_func(struct work_struct *work)
|
||||
nr_pages = atomic_read(&krcp->backoff_page_cache_fill) ?
|
||||
1 : rcu_min_cached_objs;
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
for (i = READ_ONCE(krcp->nr_bkv_objs); i < nr_pages; i++) {
|
||||
bnode = (struct kvfree_rcu_bulk_data *)
|
||||
__get_free_page(GFP_KERNEL | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
|
||||
|
||||
@@ -3218,6 +3250,10 @@ static void fill_page_cache_func(struct work_struct *work)
|
||||
static void
|
||||
run_page_cache_worker(struct kfree_rcu_cpu *krcp)
|
||||
{
|
||||
// If cache disabled, bail out.
|
||||
if (!rcu_min_cached_objs)
|
||||
return;
|
||||
|
||||
if (rcu_scheduler_active == RCU_SCHEDULER_RUNNING &&
|
||||
!atomic_xchg(&krcp->work_in_progress, 1)) {
|
||||
if (atomic_read(&krcp->backoff_page_cache_fill)) {
|
||||
@@ -3272,7 +3308,7 @@ add_ptr_to_bulk_krc_lock(struct kfree_rcu_cpu **krcp,
|
||||
// scenarios.
|
||||
bnode = (struct kvfree_rcu_bulk_data *)
|
||||
__get_free_page(GFP_KERNEL | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
|
||||
*krcp = krc_this_cpu_lock(flags);
|
||||
raw_spin_lock_irqsave(&(*krcp)->lock, *flags);
|
||||
}
|
||||
|
||||
if (!bnode)
|
||||
@@ -3285,7 +3321,7 @@ add_ptr_to_bulk_krc_lock(struct kfree_rcu_cpu **krcp,
|
||||
|
||||
// Finally insert and update the GP for this page.
|
||||
bnode->records[bnode->nr_records++] = ptr;
|
||||
bnode->gp_snap = get_state_synchronize_rcu();
|
||||
get_state_synchronize_rcu_full(&bnode->gp_snap);
|
||||
atomic_inc(&(*krcp)->bulk_count[idx]);
|
||||
|
||||
return true;
|
||||
@@ -4283,7 +4319,6 @@ int rcutree_prepare_cpu(unsigned int cpu)
|
||||
*/
|
||||
rnp = rdp->mynode;
|
||||
raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
|
||||
rdp->beenonline = true; /* We have now been online. */
|
||||
rdp->gp_seq = READ_ONCE(rnp->gp_seq);
|
||||
rdp->gp_seq_needed = rdp->gp_seq;
|
||||
rdp->cpu_no_qs.b.norm = true;
|
||||
@@ -4310,6 +4345,16 @@ static void rcutree_affinity_setting(unsigned int cpu, int outgoing)
|
||||
rcu_boost_kthread_setaffinity(rdp->mynode, outgoing);
|
||||
}
|
||||
|
||||
/*
|
||||
* Has the specified (known valid) CPU ever been fully online?
|
||||
*/
|
||||
bool rcu_cpu_beenfullyonline(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
|
||||
return smp_load_acquire(&rdp->beenonline);
|
||||
}
|
||||
|
||||
/*
|
||||
* Near the end of the CPU-online process. Pretty much all services
|
||||
* enabled, and the CPU is now very much alive.
|
||||
@@ -4368,15 +4413,16 @@ int rcutree_offline_cpu(unsigned int cpu)
|
||||
* Note that this function is special in that it is invoked directly
|
||||
* from the incoming CPU rather than from the cpuhp_step mechanism.
|
||||
* This is because this function must be invoked at a precise location.
|
||||
* This incoming CPU must not have enabled interrupts yet.
|
||||
*/
|
||||
void rcu_cpu_starting(unsigned int cpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long mask;
|
||||
struct rcu_data *rdp;
|
||||
struct rcu_node *rnp;
|
||||
bool newcpu;
|
||||
|
||||
lockdep_assert_irqs_disabled();
|
||||
rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
if (rdp->cpu_started)
|
||||
return;
|
||||
@@ -4384,7 +4430,6 @@ void rcu_cpu_starting(unsigned int cpu)
|
||||
|
||||
rnp = rdp->mynode;
|
||||
mask = rdp->grpmask;
|
||||
local_irq_save(flags);
|
||||
arch_spin_lock(&rcu_state.ofl_lock);
|
||||
rcu_dynticks_eqs_online();
|
||||
raw_spin_lock(&rcu_state.barrier_lock);
|
||||
@@ -4403,17 +4448,17 @@ void rcu_cpu_starting(unsigned int cpu)
|
||||
/* An incoming CPU should never be blocking a grace period. */
|
||||
if (WARN_ON_ONCE(rnp->qsmask & mask)) { /* RCU waiting on incoming CPU? */
|
||||
/* rcu_report_qs_rnp() *really* wants some flags to restore */
|
||||
unsigned long flags2;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags2);
|
||||
local_irq_save(flags);
|
||||
rcu_disable_urgency_upon_qs(rdp);
|
||||
/* Report QS -after- changing ->qsmaskinitnext! */
|
||||
rcu_report_qs_rnp(mask, rnp, rnp->gp_seq, flags2);
|
||||
rcu_report_qs_rnp(mask, rnp, rnp->gp_seq, flags);
|
||||
} else {
|
||||
raw_spin_unlock_rcu_node(rnp);
|
||||
}
|
||||
arch_spin_unlock(&rcu_state.ofl_lock);
|
||||
local_irq_restore(flags);
|
||||
smp_store_release(&rdp->beenonline, true);
|
||||
smp_mb(); /* Ensure RCU read-side usage follows above initialization. */
|
||||
}
|
||||
|
||||
|
@@ -643,7 +643,7 @@ static void synchronize_rcu_expedited_wait(void)
|
||||
"O."[!!cpu_online(cpu)],
|
||||
"o."[!!(rdp->grpmask & rnp->expmaskinit)],
|
||||
"N."[!!(rdp->grpmask & rnp->expmaskinitnext)],
|
||||
"D."[!!(rdp->cpu_no_qs.b.exp)]);
|
||||
"D."[!!data_race(rdp->cpu_no_qs.b.exp)]);
|
||||
}
|
||||
}
|
||||
pr_cont(" } %lu jiffies s: %lu root: %#lx/%c\n",
|
||||
|
@@ -1319,13 +1319,22 @@ lazy_rcu_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
|
||||
int cpu;
|
||||
unsigned long count = 0;
|
||||
|
||||
if (WARN_ON_ONCE(!cpumask_available(rcu_nocb_mask)))
|
||||
return 0;
|
||||
|
||||
/* Protect rcu_nocb_mask against concurrent (de-)offloading. */
|
||||
if (!mutex_trylock(&rcu_state.barrier_mutex))
|
||||
return 0;
|
||||
|
||||
/* Snapshot count of all CPUs */
|
||||
for_each_possible_cpu(cpu) {
|
||||
for_each_cpu(cpu, rcu_nocb_mask) {
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
|
||||
count += READ_ONCE(rdp->lazy_len);
|
||||
}
|
||||
|
||||
mutex_unlock(&rcu_state.barrier_mutex);
|
||||
|
||||
return count ? count : SHRINK_EMPTY;
|
||||
}
|
||||
|
||||
@@ -1336,15 +1345,45 @@ lazy_rcu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
|
||||
unsigned long flags;
|
||||
unsigned long count = 0;
|
||||
|
||||
/* Snapshot count of all CPUs */
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
int _count = READ_ONCE(rdp->lazy_len);
|
||||
if (WARN_ON_ONCE(!cpumask_available(rcu_nocb_mask)))
|
||||
return 0;
|
||||
/*
|
||||
* Protect against concurrent (de-)offloading. Otherwise nocb locking
|
||||
* may be ignored or imbalanced.
|
||||
*/
|
||||
if (!mutex_trylock(&rcu_state.barrier_mutex)) {
|
||||
/*
|
||||
* But really don't insist if barrier_mutex is contended since we
|
||||
* can't guarantee that it will never engage in a dependency
|
||||
* chain involving memory allocation. The lock is seldom contended
|
||||
* anyway.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_count == 0)
|
||||
/* Snapshot count of all CPUs */
|
||||
for_each_cpu(cpu, rcu_nocb_mask) {
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
int _count;
|
||||
|
||||
if (WARN_ON_ONCE(!rcu_rdp_is_offloaded(rdp)))
|
||||
continue;
|
||||
|
||||
if (!READ_ONCE(rdp->lazy_len))
|
||||
continue;
|
||||
|
||||
rcu_nocb_lock_irqsave(rdp, flags);
|
||||
WRITE_ONCE(rdp->lazy_len, 0);
|
||||
/*
|
||||
* Recheck under the nocb lock. Since we are not holding the bypass
|
||||
* lock we may still race with increments from the enqueuer but still
|
||||
* we know for sure if there is at least one lazy callback.
|
||||
*/
|
||||
_count = READ_ONCE(rdp->lazy_len);
|
||||
if (!_count) {
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
continue;
|
||||
}
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies, false));
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
wake_nocb_gp(rdp, false);
|
||||
sc->nr_to_scan -= _count;
|
||||
@@ -1352,6 +1391,9 @@ lazy_rcu_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
|
||||
if (sc->nr_to_scan <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&rcu_state.barrier_mutex);
|
||||
|
||||
return count ? count : SHRINK_STOP;
|
||||
}
|
||||
|
||||
|
@@ -257,6 +257,8 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
* GP should not be able to end until we report, so there should be
|
||||
* no need to check for a subsequent expedited GP. (Though we are
|
||||
* still in a quiescent state in any case.)
|
||||
*
|
||||
* Interrupts are disabled, so ->cpu_no_qs.b.exp cannot change.
|
||||
*/
|
||||
if (blkd_state & RCU_EXP_BLKD && rdp->cpu_no_qs.b.exp)
|
||||
rcu_report_exp_rdp(rdp);
|
||||
@@ -941,7 +943,7 @@ notrace void rcu_preempt_deferred_qs(struct task_struct *t)
|
||||
{
|
||||
struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
|
||||
|
||||
if (rdp->cpu_no_qs.b.exp)
|
||||
if (READ_ONCE(rdp->cpu_no_qs.b.exp))
|
||||
rcu_report_exp_rdp(rdp);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user