mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	rcutorture: Abstract stutter_wait()
Because stuttering the test load (stopping and restarting it) is useful for non-RCU testing, this commit moves the load-stuttering functionality to kernel/torture.c. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Reviewed-by: Josh Triplett <josh@joshtriplett.org>
This commit is contained in:
		
							parent
							
								
									fac480efcb
								
							
						
					
					
						commit
						628edaa506
					
				
					 3 changed files with 104 additions and 59 deletions
				
			
		| 
						 | 
				
			
			@ -75,8 +75,13 @@ int torture_shuffle_init(long shuffint);
 | 
			
		|||
/* Shutdown task absorption, for when the tasks cannot safely be killed. */
 | 
			
		||||
void torture_shutdown_absorb(const char *title);
 | 
			
		||||
 | 
			
		||||
/* Task stuttering, which forces load/no-load transitions. */
 | 
			
		||||
void stutter_wait(const char *title);
 | 
			
		||||
int torture_stutter_init(int s);
 | 
			
		||||
void torture_stutter_cleanup(void);
 | 
			
		||||
 | 
			
		||||
/* Initialization and cleanup. */
 | 
			
		||||
void torture_init_begin(char *ttype, bool v);
 | 
			
		||||
void torture_init_begin(char *ttype, bool v, int *runnable);
 | 
			
		||||
void torture_init_end(void);
 | 
			
		||||
bool torture_cleanup(void);
 | 
			
		||||
bool torture_must_stop(void);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,7 +103,6 @@ static struct task_struct *writer_task;
 | 
			
		|||
static struct task_struct **fakewriter_tasks;
 | 
			
		||||
static struct task_struct **reader_tasks;
 | 
			
		||||
static struct task_struct *stats_task;
 | 
			
		||||
static struct task_struct *stutter_task;
 | 
			
		||||
static struct task_struct *fqs_task;
 | 
			
		||||
static struct task_struct *boost_tasks[NR_CPUS];
 | 
			
		||||
static struct task_struct *shutdown_task;
 | 
			
		||||
| 
						 | 
				
			
			@ -145,8 +144,6 @@ static long n_barrier_attempts;
 | 
			
		|||
static long n_barrier_successes;
 | 
			
		||||
static struct list_head rcu_torture_removed;
 | 
			
		||||
 | 
			
		||||
static int stutter_pause_test;
 | 
			
		||||
 | 
			
		||||
#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE)
 | 
			
		||||
#define RCUTORTURE_RUNNABLE_INIT 1
 | 
			
		||||
#else
 | 
			
		||||
| 
						 | 
				
			
			@ -222,18 +219,6 @@ rcu_torture_free(struct rcu_torture *p)
 | 
			
		|||
	spin_unlock_bh(&rcu_torture_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
rcu_stutter_wait(const char *title)
 | 
			
		||||
{
 | 
			
		||||
	while (stutter_pause_test || !rcutorture_runnable) {
 | 
			
		||||
		if (rcutorture_runnable)
 | 
			
		||||
			schedule_timeout_interruptible(1);
 | 
			
		||||
		else
 | 
			
		||||
			schedule_timeout_interruptible(round_jiffies_relative(HZ));
 | 
			
		||||
		torture_shutdown_absorb(title);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Operations vector for selecting different types of tests.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -571,7 +556,7 @@ static int rcu_torture_boost(void *arg)
 | 
			
		|||
		oldstarttime = boost_starttime;
 | 
			
		||||
		while (ULONG_CMP_LT(jiffies, oldstarttime)) {
 | 
			
		||||
			schedule_timeout_interruptible(oldstarttime - jiffies);
 | 
			
		||||
			rcu_stutter_wait("rcu_torture_boost");
 | 
			
		||||
			stutter_wait("rcu_torture_boost");
 | 
			
		||||
			if (torture_must_stop())
 | 
			
		||||
				goto checkwait;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -593,7 +578,7 @@ static int rcu_torture_boost(void *arg)
 | 
			
		|||
				call_rcu_time = jiffies;
 | 
			
		||||
			}
 | 
			
		||||
			cond_resched();
 | 
			
		||||
			rcu_stutter_wait("rcu_torture_boost");
 | 
			
		||||
			stutter_wait("rcu_torture_boost");
 | 
			
		||||
			if (torture_must_stop())
 | 
			
		||||
				goto checkwait;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -618,7 +603,7 @@ static int rcu_torture_boost(void *arg)
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		/* Go do the stutter. */
 | 
			
		||||
checkwait:	rcu_stutter_wait("rcu_torture_boost");
 | 
			
		||||
checkwait:	stutter_wait("rcu_torture_boost");
 | 
			
		||||
	} while (!torture_must_stop());
 | 
			
		||||
 | 
			
		||||
	/* Clean up and exit. */
 | 
			
		||||
| 
						 | 
				
			
			@ -656,7 +641,7 @@ rcu_torture_fqs(void *arg)
 | 
			
		|||
			udelay(fqs_holdoff);
 | 
			
		||||
			fqs_burst_remaining -= fqs_holdoff;
 | 
			
		||||
		}
 | 
			
		||||
		rcu_stutter_wait("rcu_torture_fqs");
 | 
			
		||||
		stutter_wait("rcu_torture_fqs");
 | 
			
		||||
	} while (!torture_must_stop());
 | 
			
		||||
	VERBOSE_TOROUT_STRING("rcu_torture_fqs task stopping");
 | 
			
		||||
	torture_shutdown_absorb("rcu_torture_fqs");
 | 
			
		||||
| 
						 | 
				
			
			@ -728,7 +713,7 @@ rcu_torture_writer(void *arg)
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
		rcutorture_record_progress(++rcu_torture_current_version);
 | 
			
		||||
		rcu_stutter_wait("rcu_torture_writer");
 | 
			
		||||
		stutter_wait("rcu_torture_writer");
 | 
			
		||||
	} while (!torture_must_stop());
 | 
			
		||||
	VERBOSE_TOROUT_STRING("rcu_torture_writer task stopping");
 | 
			
		||||
	torture_shutdown_absorb("rcu_torture_writer");
 | 
			
		||||
| 
						 | 
				
			
			@ -765,7 +750,7 @@ rcu_torture_fakewriter(void *arg)
 | 
			
		|||
		} else {
 | 
			
		||||
			cur_ops->exp_sync();
 | 
			
		||||
		}
 | 
			
		||||
		rcu_stutter_wait("rcu_torture_fakewriter");
 | 
			
		||||
		stutter_wait("rcu_torture_fakewriter");
 | 
			
		||||
	} while (!torture_must_stop());
 | 
			
		||||
 | 
			
		||||
	VERBOSE_TOROUT_STRING("rcu_torture_fakewriter task stopping");
 | 
			
		||||
| 
						 | 
				
			
			@ -910,7 +895,7 @@ rcu_torture_reader(void *arg)
 | 
			
		|||
		preempt_enable();
 | 
			
		||||
		cur_ops->readunlock(idx);
 | 
			
		||||
		schedule();
 | 
			
		||||
		rcu_stutter_wait("rcu_torture_reader");
 | 
			
		||||
		stutter_wait("rcu_torture_reader");
 | 
			
		||||
	} while (!torture_must_stop());
 | 
			
		||||
	VERBOSE_TOROUT_STRING("rcu_torture_reader task stopping");
 | 
			
		||||
	torture_shutdown_absorb("rcu_torture_reader");
 | 
			
		||||
| 
						 | 
				
			
			@ -1034,25 +1019,6 @@ rcu_torture_stats(void *arg)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Cause the rcutorture test to "stutter", starting and stopping all
 | 
			
		||||
 * threads periodically.
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
rcu_torture_stutter(void *arg)
 | 
			
		||||
{
 | 
			
		||||
	VERBOSE_TOROUT_STRING("rcu_torture_stutter task started");
 | 
			
		||||
	do {
 | 
			
		||||
		schedule_timeout_interruptible(stutter * HZ);
 | 
			
		||||
		stutter_pause_test = 1;
 | 
			
		||||
		if (!kthread_should_stop())
 | 
			
		||||
			schedule_timeout_interruptible(stutter * HZ);
 | 
			
		||||
		stutter_pause_test = 0;
 | 
			
		||||
		torture_shutdown_absorb("rcu_torture_stutter");
 | 
			
		||||
	} while (!kthread_should_stop());
 | 
			
		||||
	VERBOSE_TOROUT_STRING("rcu_torture_stutter task stopping");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1403,11 +1369,7 @@ rcu_torture_cleanup(void)
 | 
			
		|||
 | 
			
		||||
	rcu_torture_barrier_cleanup();
 | 
			
		||||
	rcu_torture_stall_cleanup();
 | 
			
		||||
	if (stutter_task) {
 | 
			
		||||
		VERBOSE_TOROUT_STRING("Stopping rcu_torture_stutter task");
 | 
			
		||||
		kthread_stop(stutter_task);
 | 
			
		||||
	}
 | 
			
		||||
	stutter_task = NULL;
 | 
			
		||||
	torture_stutter_cleanup();
 | 
			
		||||
 | 
			
		||||
	if (writer_task) {
 | 
			
		||||
		VERBOSE_TOROUT_STRING("Stopping rcu_torture_writer task");
 | 
			
		||||
| 
						 | 
				
			
			@ -1548,7 +1510,7 @@ rcu_torture_init(void)
 | 
			
		|||
		&rcu_ops, &rcu_bh_ops, &srcu_ops, &sched_ops,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	torture_init_begin(torture_type, verbose);
 | 
			
		||||
	torture_init_begin(torture_type, verbose, &rcutorture_runnable);
 | 
			
		||||
 | 
			
		||||
	/* Process args and tell the world that the torturer is on the job. */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1682,21 +1644,14 @@ rcu_torture_init(void)
 | 
			
		|||
	if (stutter < 0)
 | 
			
		||||
		stutter = 0;
 | 
			
		||||
	if (stutter) {
 | 
			
		||||
		/* Create the stutter thread */
 | 
			
		||||
		stutter_task = kthread_run(rcu_torture_stutter, NULL,
 | 
			
		||||
					  "rcu_torture_stutter");
 | 
			
		||||
		if (IS_ERR(stutter_task)) {
 | 
			
		||||
			firsterr = PTR_ERR(stutter_task);
 | 
			
		||||
			VERBOSE_TOROUT_ERRSTRING("Failed to create stutter");
 | 
			
		||||
			stutter_task = NULL;
 | 
			
		||||
		firsterr = torture_stutter_init(stutter * HZ);
 | 
			
		||||
		if (firsterr)
 | 
			
		||||
			goto unwind;
 | 
			
		||||
	}
 | 
			
		||||
		torture_shuffle_task_register(stutter_task);
 | 
			
		||||
	}
 | 
			
		||||
	if (fqs_duration < 0)
 | 
			
		||||
		fqs_duration = 0;
 | 
			
		||||
	if (fqs_duration) {
 | 
			
		||||
		/* Create the stutter thread */
 | 
			
		||||
		/* Create the fqs thread */
 | 
			
		||||
		fqs_task = kthread_run(rcu_torture_fqs, NULL,
 | 
			
		||||
				       "rcu_torture_fqs");
 | 
			
		||||
		if (IS_ERR(fqs_task)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,6 +58,7 @@ static bool verbose;
 | 
			
		|||
#define FULLSTOP_RMMOD    2	/* Normal rmmod of torture. */
 | 
			
		||||
static int fullstop = FULLSTOP_RMMOD;
 | 
			
		||||
static DEFINE_MUTEX(fullstop_mutex);
 | 
			
		||||
static int *torture_runnable;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_HOTPLUG_CPU
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -452,17 +453,101 @@ static struct notifier_block torture_shutdown_nb = {
 | 
			
		|||
	.notifier_call = torture_shutdown_notify,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Variables for stuttering, which means to periodically pause and
 | 
			
		||||
 * restart testing in order to catch bugs that appear when load is
 | 
			
		||||
 * suddenly applied to or removed from the system.
 | 
			
		||||
 */
 | 
			
		||||
static struct task_struct *stutter_task;
 | 
			
		||||
static int stutter_pause_test;
 | 
			
		||||
static int stutter;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Block until the stutter interval ends.  This must be called periodically
 | 
			
		||||
 * by all running kthreads that need to be subject to stuttering.
 | 
			
		||||
 */
 | 
			
		||||
void stutter_wait(const char *title)
 | 
			
		||||
{
 | 
			
		||||
	while (ACCESS_ONCE(stutter_pause_test) ||
 | 
			
		||||
	       (torture_runnable && !ACCESS_ONCE(*torture_runnable))) {
 | 
			
		||||
		if (stutter_pause_test)
 | 
			
		||||
			schedule_timeout_interruptible(1);
 | 
			
		||||
		else
 | 
			
		||||
			schedule_timeout_interruptible(round_jiffies_relative(HZ));
 | 
			
		||||
		torture_shutdown_absorb(title);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(stutter_wait);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Cause the torture test to "stutter", starting and stopping all
 | 
			
		||||
 * threads periodically.
 | 
			
		||||
 */
 | 
			
		||||
static int torture_stutter(void *arg)
 | 
			
		||||
{
 | 
			
		||||
	VERBOSE_TOROUT_STRING("torture_stutter task started");
 | 
			
		||||
	do {
 | 
			
		||||
		if (!torture_must_stop()) {
 | 
			
		||||
			schedule_timeout_interruptible(stutter);
 | 
			
		||||
			ACCESS_ONCE(stutter_pause_test) = 1;
 | 
			
		||||
		}
 | 
			
		||||
		if (!torture_must_stop())
 | 
			
		||||
			schedule_timeout_interruptible(stutter);
 | 
			
		||||
		ACCESS_ONCE(stutter_pause_test) = 0;
 | 
			
		||||
		torture_shutdown_absorb("torture_stutter");
 | 
			
		||||
	} while (!torture_must_stop());
 | 
			
		||||
	VERBOSE_TOROUT_STRING("torture_stutter task stopping");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize and kick off the torture_stutter kthread.
 | 
			
		||||
 */
 | 
			
		||||
int torture_stutter_init(int s)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	stutter = s;
 | 
			
		||||
	stutter_task = kthread_run(torture_stutter, NULL, "torture_stutter");
 | 
			
		||||
	if (IS_ERR(stutter_task)) {
 | 
			
		||||
		ret = PTR_ERR(stutter_task);
 | 
			
		||||
		VERBOSE_TOROUT_ERRSTRING("Failed to create stutter");
 | 
			
		||||
		stutter_task = NULL;
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	torture_shuffle_task_register(stutter_task);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(torture_stutter_init);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Cleanup after the torture_stutter kthread.
 | 
			
		||||
 */
 | 
			
		||||
void torture_stutter_cleanup(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!stutter_task)
 | 
			
		||||
		return;
 | 
			
		||||
	VERBOSE_TOROUT_STRING("Stopping torture_stutter task");
 | 
			
		||||
	kthread_stop(stutter_task);
 | 
			
		||||
	stutter_task = NULL;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(torture_stutter_cleanup);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Initialize torture module.  Please note that this is -not- invoked via
 | 
			
		||||
 * the usual module_init() mechanism, but rather by an explicit call from
 | 
			
		||||
 * the client torture module.  This call must be paired with a later
 | 
			
		||||
 * torture_init_end().
 | 
			
		||||
 *
 | 
			
		||||
 * The runnable parameter points to a flag that controls whether or not
 | 
			
		||||
 * the test is currently runnable.  If there is no such flag, pass in NULL.
 | 
			
		||||
 */
 | 
			
		||||
void __init torture_init_begin(char *ttype, bool v)
 | 
			
		||||
void __init torture_init_begin(char *ttype, bool v, int *runnable)
 | 
			
		||||
{
 | 
			
		||||
	mutex_lock(&fullstop_mutex);
 | 
			
		||||
	torture_type = ttype;
 | 
			
		||||
	verbose = v;
 | 
			
		||||
	torture_runnable = runnable;
 | 
			
		||||
	fullstop = FULLSTOP_DONTSTOP;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue