/* * The intended use cases for the nreaders and nwriters module parameters * are as follows: * * 1. Specify only the nr_cpus kernel boot parameter. This will * set both nreaders and nwriters to the value specified by * nr_cpus for a mixed reader/writer test. * * 2. Specify the nr_cpus kernel boot parameter, but set * rcuscale.nreaders to zero. This will set nwriters to the * value specified by nr_cpus for an update-only test. * * 3. Specify the nr_cpus kernel boot parameter, but set * rcuscale.nwriters to zero. This will set nreaders to the * value specified by nr_cpus for a read-only test. * * Various other use cases may of course be specified. * * Note that this test's readers are intended only as a test load for * the writers. The reader scalability statistics will be overly * pessimistic due to the per-critical-section interrupt disabling, * test-end checks, and the pair of calls through pointers.
*/
torture_param(bool, gp_async, false, "Use asynchronous GP wait primitives");
torture_param(int, gp_async_max, 1000, "Max # outstanding waits per writer");
torture_param(bool, gp_exp, false, "Use expedited GP wait primitives");
torture_param(int, holdoff, 10, "Holdoff time before test start (s)");
torture_param(int, minruntime, 0, "Minimum run time (s)");
torture_param(int, nreaders, -1, "Number of RCU reader threads");
torture_param(int, nwriters, -1, "Number of RCU updater threads");
torture_param(bool, shutdown, RCUSCALE_SHUTDOWN, "Shutdown at end of scalability tests.");
torture_param(int, verbose, 1, "Enable verbose debugging printk()s");
torture_param(int, writer_holdoff, 0, "Holdoff (us) between GPs, zero to disable");
torture_param(int, writer_holdoff_jiffies, 0, "Holdoff (jiffies) between GPs, zero to disable");
torture_param(int, kfree_rcu_test, 0, "Do we run a kfree_rcu() scale test?");
torture_param(int, kfree_mult, 1, "Multiple of kfree_obj size to allocate.");
torture_param(int, kfree_by_call_rcu, 0, "Use call_rcu() to emulate kfree_rcu()?");
staticchar *scale_type = "rcu";
module_param(scale_type, charp, 0444);
MODULE_PARM_DESC(scale_type, "Type of RCU to scalability-test (rcu, srcu, ...)");
/* * If scalability tests complete, wait for shutdown to commence.
*/ staticvoid rcu_scale_wait_shutdown(void)
{
cond_resched_tasks_rcu_qs(); if (atomic_read(&n_rcu_scale_writer_finished) < nrealwriters) return; while (!torture_must_stop())
schedule_timeout_uninterruptible(1);
}
/* * RCU scalability reader kthread. Repeatedly does empty RCU read-side * critical section, minimizing update-side interference. However, the * point of this test is not to evaluate reader scalability, but instead * to serve as a test load for update-side scalability testing.
*/ staticint
rcu_scale_reader(void *arg)
{ unsignedlong flags; int idx; long me = (long)arg;
/* * Wait until rcu_end_inkernel_boot() is called for normal GP tests * so that RCU is not always expedited for normal GP tests. * The system_state test is approximate, but works well in practice.
*/ while (!gp_exp && system_state != SYSTEM_RUNNING)
schedule_timeout_uninterruptible(1);
t = ktime_get_mono_fast_ns(); if (atomic_inc_return(&n_rcu_scale_writer_started) >= nrealwriters) {
t_rcu_scale_writer_started = t; if (gp_exp) {
b_rcu_gp_test_started =
cur_ops->exp_completed() / 2;
} else {
b_rcu_gp_test_started = cur_ops->get_gp_seq();
}
}
/* * 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 * at least one.
*/ staticint compute_real(int n)
{ int nr;
if (n >= 0) {
nr = n;
} else {
nr = num_online_cpus() + 1 + n; if (nr <= 0)
nr = 1;
} return nr;
}
/* * 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.
*/
torture_param(int, kfree_nthreads, -1, "Number of threads running loops of kfree_rcu().");
torture_param(int, kfree_alloc_num, 8000, "Number of allocations and frees done in an iteration.");
torture_param(int, kfree_loops, 10, "Number of loops doing kfree_alloc_num allocations and frees.");
torture_param(bool, kfree_rcu_test_double, false, "Do we run a kfree_rcu() double-argument scale test?");
torture_param(bool, kfree_rcu_test_single, false, "Do we run a kfree_rcu() single-argument scale test?");
for (i = 0; i < kfree_alloc_num; i++) {
alloc_ptr = kcalloc(kfree_mult, sizeof(struct kfree_obj), GFP_KERNEL); if (!alloc_ptr) return -ENOMEM;
if (kfree_by_call_rcu) {
call_rcu(&(alloc_ptr->rh), kfree_call_rcu); continue;
}
// By default kfree_rcu_test_single and kfree_rcu_test_double are // initialized to false. If both have the same value (false or true) // both are randomly tested, otherwise only the one with value true // is tested. if ((kfree_rcu_test_single && !kfree_rcu_test_double) ||
(kfree_rcu_test_both && torture_random(&tr) & 0x800))
kfree_rcu_mightsleep(alloc_ptr); else
kfree_rcu(alloc_ptr, rh);
}
cond_resched();
} while (!torture_must_stop() && ++loop < kfree_loops);
if (atomic_inc_return(&n_kfree_scale_thread_ended) >= kfree_nrealthreads) {
end_time = ktime_get_mono_fast_ns();
if (kfree_reader_tasks) { for (i = 0; i < kfree_nrealthreads; i++)
torture_stop_kthread(kfree_scale_thread,
kfree_reader_tasks[i]);
kfree(kfree_reader_tasks);
kfree_reader_tasks = NULL;
}
torture_cleanup_end();
}
/* * shutdown kthread. Just waits to be awakened, then shuts down system.
*/ staticint
kfree_scale_shutdown(void *arg)
{
wait_event_idle(shutdown_wq,
atomic_read(&n_kfree_scale_thread_ended) >= kfree_nrealthreads);
// Also, do a quick self-test to ensure laziness is as much as // expected. if (kfree_by_call_rcu && !IS_ENABLED(CONFIG_RCU_LAZY)) {
pr_alert("CONFIG_RCU_LAZY is disabled, falling back to kfree_rcu() for delayed RCU kfree'ing\n");
kfree_by_call_rcu = 0;
}
if (kfree_by_call_rcu) { /* do a test to check the timeout. */
orig_jif = rcu_get_jiffies_lazy_flush();
smp_cond_load_relaxed(&rcu_lazy_test1_cb_called, VAL == 1);
rcu_set_jiffies_lazy_flush(orig_jif);
if (WARN_ON_ONCE(jiffies_at_lazy_cb - jif_start < 2 * HZ)) {
pr_alert("ERROR: call_rcu() CBs are not being lazy as expected!\n");
firsterr = -1; goto unwind;
}
if (WARN_ON_ONCE(jiffies_at_lazy_cb - jif_start > 3 * HZ)) {
pr_alert("ERROR: call_rcu() CBs are being too lazy!\n");
firsterr = -1; goto unwind;
}
}
kfree_nrealthreads = compute_real(kfree_nthreads); /* Start up the kthreads. */ if (shutdown) {
init_waitqueue_head(&shutdown_wq);
firsterr = torture_create_kthread(kfree_scale_shutdown, NULL,
shutdown_task); if (torture_init_error(firsterr)) goto unwind;
schedule_timeout_uninterruptible(1);
}
for (i = 0; i < kfree_nrealthreads; i++) {
firsterr = torture_create_kthread(kfree_scale_thread, (void *)i,
kfree_reader_tasks[i]); if (torture_init_error(firsterr)) goto unwind;
}
while (atomic_read(&n_kfree_scale_thread_started) < kfree_nrealthreads)
schedule_timeout_uninterruptible(1);
staticvoid
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 built-in, just report all of the GP kthread's CPU time. if (IS_BUILTIN(CONFIG_RCU_SCALE_TEST) && !kthread_tp && cur_ops->rso_gp_kthread)
kthread_tp = cur_ops->rso_gp_kthread(); if (kthread_tp) {
u32 ns;
u64 us;
kthread_stime = kthread_tp->stime - kthread_stime;
us = div_u64_rem(kthread_stime, 1000, &ns);
pr_info("rcu_scale: Grace-period kthread CPU time: %llu.%03u us\n", us, ns);
show_rcu_gp_kthreads();
} 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);
reader_tasks = NULL;
}
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]); if (writer_freelists) { int ctr = 0; struct llist_node *llnp; struct writer_freelist *wflp = &writer_freelists[i];
/* 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.
*/ staticint
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;
}
staticint __init
rcu_scale_init(void)
{ int firsterr = 0; long i; long j; staticstruct rcu_scale_ops *scale_ops[] = {
&rcu_ops, &srcu_ops, &srcud_ops, TASKS_OPS TASKS_RUDE_OPS TASKS_TRACING_OPS
};
if (!torture_init_begin(scale_type, verbose)) return -EBUSY;
/* Process args and announce that the scalability'er is on the job. */ for (i = 0; i < ARRAY_SIZE(scale_ops); i++) {
cur_ops = scale_ops[i]; if (strcmp(scale_type, cur_ops->name) == 0) break;
} if (i == ARRAY_SIZE(scale_ops)) {
pr_alert("rcu-scale: invalid scale type: \"%s\"\n", scale_type);
pr_alert("rcu-scale types:"); for (i = 0; i < ARRAY_SIZE(scale_ops); i++)
pr_cont(" %s", scale_ops[i]->name);
pr_cont("\n");
firsterr = -EINVAL;
cur_ops = NULL; goto unwind;
} if (cur_ops->init)
cur_ops->init();
if (cur_ops->rso_gp_kthread) {
kthread_tp = cur_ops->rso_gp_kthread(); if (kthread_tp)
kthread_stime = kthread_tp->stime;
} if (kfree_rcu_test) return kfree_scale_init();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.