In addition to the existing timing-based (holdoff, writer_holdoff) start control, add file-based controls to debugfs. This patch adds an option "block_start", which holds all worker threads until the "rcuscale/should_start" debugfs file is written with a non-zero integer. A new "test_complete" file is added to the debugfs folder, with file content "0" indicating experiment has not finished and "1" indicating finished. This is useful for start/finish control by external test tools. Signed-off-by: Yuzhuo Jing <yuzhuo@xxxxxxxxxx> --- .../admin-guide/kernel-parameters.txt | 5 ++ kernel/rcu/rcuscale.c | 79 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 7b62a84a19d4..5e233e511f81 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5487,6 +5487,11 @@ Default is 0. + rcuscale.block_start= [KNL] + Block the experiment start until "1" is written to the + rcuscale/should_start file in debugfs. This is useful + for start/finish control by external tools. + rcuscale.gp_async= [KNL] Measure performance of asynchronous grace-period primitives such as call_rcu(). diff --git a/kernel/rcu/rcuscale.c b/kernel/rcu/rcuscale.c index 7c88d461ed2c..43bcaeac457f 100644 --- a/kernel/rcu/rcuscale.c +++ b/kernel/rcu/rcuscale.c @@ -87,6 +87,7 @@ MODULE_AUTHOR("Paul E. McKenney <paulmck@xxxxxxxxxxxxx>"); # define RCUSCALE_SHUTDOWN 1 #endif +torture_param(bool, block_start, false, "Block all threads after creation and wait for should_start"); 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"); @@ -146,6 +147,12 @@ static struct dentry *debugfs_writer_durations; static struct dentry *debugfs_reader_tasks; static struct dentry *debugfs_writer_tasks; static struct dentry *debugfs_kfree_tasks; +static struct dentry *debugfs_should_start; +static struct dentry *debugfs_test_complete; + +static DECLARE_COMPLETION(start_barrier); +static bool should_start; +static bool test_complete; #define MAX_MEAS 10000 #define MIN_MEAS 100 @@ -457,6 +464,23 @@ static void rcu_scale_wait_shutdown(void) schedule_timeout_uninterruptible(1); } +/* + * Wait start_barrier if block_start is enabled. Exit early if shutdown + * is requested. + * + * Return: true if caller should exit; false if caller should continue. + */ +static bool wait_start_barrier(void) +{ + if (!block_start) + return false; + while (wait_for_completion_interruptible(&start_barrier)) { + if (torture_must_stop()) + return true; + } + return false; +} + /* * RCU scalability reader kthread. Repeatedly does empty RCU read-side * critical section, minimizing update-side interference. However, the @@ -475,6 +499,11 @@ rcu_scale_reader(void *arg) set_user_nice(current, MAX_NICE); atomic_inc(&n_rcu_scale_reader_started); + if (wait_start_barrier()) { + torture_kthread_stopping("rcu_scale_reader"); + return 0; + } + do { local_irq_save(flags); idx = cur_ops->readlock(); @@ -560,6 +589,11 @@ rcu_scale_writer(void *arg) current->flags |= PF_NO_SETAFFINITY; sched_set_fifo_low(current); + if (wait_start_barrier()) { + torture_kthread_stopping("rcu_scale_writer"); + return 0; + } + if (holdoff) schedule_timeout_idle(holdoff * HZ); @@ -755,6 +789,11 @@ kfree_scale_thread(void *arg) set_user_nice(current, MAX_NICE); kfree_rcu_test_both = (kfree_rcu_test_single == kfree_rcu_test_double); + if (wait_start_barrier()) { + torture_kthread_stopping("kfree_scale_thread"); + return 0; + } + start_time = ktime_get_mono_fast_ns(); if (atomic_inc_return(&n_kfree_scale_thread_started) >= kfree_nrealthreads) { @@ -1118,6 +1157,32 @@ static const struct file_operations kfrees_fops = { .release = seq_release, }; +/* + * For the "should_start" writable file, reuse debugfs integer parsing, but + * override write function to also send complete_all if should_start is + * changed to 1. + * + * Any non-zero value written to this file is converted to 1. + */ +static int should_start_set(void *data, u64 val) +{ + *(bool *)data = !!val; + + if (block_start && !!val) + complete_all(&start_barrier); + + return 0; +} + +static int bool_get(void *data, u64 *val) +{ + *val = *(bool *)data; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(should_start_fops, bool_get, should_start_set, "%llu"); +DEFINE_DEBUGFS_ATTRIBUTE(test_complete_fops, bool_get, NULL, "%llu"); + /* * Create an rcuscale directory exposing run states and results. */ @@ -1153,6 +1218,15 @@ static int register_debugfs(void) debugfs_dir, NULL, &kfrees_fops)) goto fail; + if (try_create_file(debugfs_should_start, "should_start", 0644, + debugfs_dir, &should_start, &should_start_fops)) + goto fail; + + /* Future: add notification method for readers waiting on file change. */ + if (try_create_file(debugfs_test_complete, "test_complete", 0444, + debugfs_dir, &test_complete, &test_complete_fops)) + goto fail; + return 0; fail: pr_err("rcu-scale: Failed to create debugfs file."); @@ -1176,6 +1250,8 @@ do { \ try_remove(debugfs_reader_tasks); try_remove(debugfs_writer_tasks); try_remove(debugfs_kfree_tasks); + try_remove(debugfs_should_start); + try_remove(debugfs_test_complete); /* Remove directory after files. */ try_remove(debugfs_dir); @@ -1372,6 +1448,9 @@ rcu_scale_init(void) atomic_set(&n_rcu_scale_writer_finished, 0); rcu_scale_print_module_parms(cur_ops, "Start of test"); + if (!block_start) + should_start = true; + /* Start up the kthreads. */ if (shutdown) { -- 2.50.1.552.g942d659e1b-goog