Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 628edaa5 authored by Paul E. McKenney's avatar Paul E. McKenney
Browse files

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: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: default avatarJosh Triplett <josh@joshtriplett.org>
parent fac480ef
Loading
Loading
Loading
Loading
+6 −1
Original line number Original line Diff line number Diff line
@@ -75,8 +75,13 @@ int torture_shuffle_init(long shuffint);
/* Shutdown task absorption, for when the tasks cannot safely be killed. */
/* Shutdown task absorption, for when the tasks cannot safely be killed. */
void torture_shutdown_absorb(const char *title);
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. */
/* 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);
void torture_init_end(void);
bool torture_cleanup(void);
bool torture_cleanup(void);
bool torture_must_stop(void);
bool torture_must_stop(void);
+12 −57
Original line number Original line Diff line number Diff line
@@ -103,7 +103,6 @@ static struct task_struct *writer_task;
static struct task_struct **fakewriter_tasks;
static struct task_struct **fakewriter_tasks;
static struct task_struct **reader_tasks;
static struct task_struct **reader_tasks;
static struct task_struct *stats_task;
static struct task_struct *stats_task;
static struct task_struct *stutter_task;
static struct task_struct *fqs_task;
static struct task_struct *fqs_task;
static struct task_struct *boost_tasks[NR_CPUS];
static struct task_struct *boost_tasks[NR_CPUS];
static struct task_struct *shutdown_task;
static struct task_struct *shutdown_task;
@@ -145,8 +144,6 @@ static long n_barrier_attempts;
static long n_barrier_successes;
static long n_barrier_successes;
static struct list_head rcu_torture_removed;
static struct list_head rcu_torture_removed;


static int stutter_pause_test;

#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE)
#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE)
#define RCUTORTURE_RUNNABLE_INIT 1
#define RCUTORTURE_RUNNABLE_INIT 1
#else
#else
@@ -222,18 +219,6 @@ rcu_torture_free(struct rcu_torture *p)
	spin_unlock_bh(&rcu_torture_lock);
	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.
 * Operations vector for selecting different types of tests.
 */
 */
@@ -571,7 +556,7 @@ static int rcu_torture_boost(void *arg)
		oldstarttime = boost_starttime;
		oldstarttime = boost_starttime;
		while (ULONG_CMP_LT(jiffies, oldstarttime)) {
		while (ULONG_CMP_LT(jiffies, oldstarttime)) {
			schedule_timeout_interruptible(oldstarttime - jiffies);
			schedule_timeout_interruptible(oldstarttime - jiffies);
			rcu_stutter_wait("rcu_torture_boost");
			stutter_wait("rcu_torture_boost");
			if (torture_must_stop())
			if (torture_must_stop())
				goto checkwait;
				goto checkwait;
		}
		}
@@ -593,7 +578,7 @@ static int rcu_torture_boost(void *arg)
				call_rcu_time = jiffies;
				call_rcu_time = jiffies;
			}
			}
			cond_resched();
			cond_resched();
			rcu_stutter_wait("rcu_torture_boost");
			stutter_wait("rcu_torture_boost");
			if (torture_must_stop())
			if (torture_must_stop())
				goto checkwait;
				goto checkwait;
		}
		}
@@ -618,7 +603,7 @@ static int rcu_torture_boost(void *arg)
		}
		}


		/* Go do the stutter. */
		/* Go do the stutter. */
checkwait:	rcu_stutter_wait("rcu_torture_boost");
checkwait:	stutter_wait("rcu_torture_boost");
	} while (!torture_must_stop());
	} while (!torture_must_stop());


	/* Clean up and exit. */
	/* Clean up and exit. */
@@ -656,7 +641,7 @@ rcu_torture_fqs(void *arg)
			udelay(fqs_holdoff);
			udelay(fqs_holdoff);
			fqs_burst_remaining -= fqs_holdoff;
			fqs_burst_remaining -= fqs_holdoff;
		}
		}
		rcu_stutter_wait("rcu_torture_fqs");
		stutter_wait("rcu_torture_fqs");
	} while (!torture_must_stop());
	} while (!torture_must_stop());
	VERBOSE_TOROUT_STRING("rcu_torture_fqs task stopping");
	VERBOSE_TOROUT_STRING("rcu_torture_fqs task stopping");
	torture_shutdown_absorb("rcu_torture_fqs");
	torture_shutdown_absorb("rcu_torture_fqs");
@@ -728,7 +713,7 @@ rcu_torture_writer(void *arg)
			}
			}
		}
		}
		rcutorture_record_progress(++rcu_torture_current_version);
		rcutorture_record_progress(++rcu_torture_current_version);
		rcu_stutter_wait("rcu_torture_writer");
		stutter_wait("rcu_torture_writer");
	} while (!torture_must_stop());
	} while (!torture_must_stop());
	VERBOSE_TOROUT_STRING("rcu_torture_writer task stopping");
	VERBOSE_TOROUT_STRING("rcu_torture_writer task stopping");
	torture_shutdown_absorb("rcu_torture_writer");
	torture_shutdown_absorb("rcu_torture_writer");
@@ -765,7 +750,7 @@ rcu_torture_fakewriter(void *arg)
		} else {
		} else {
			cur_ops->exp_sync();
			cur_ops->exp_sync();
		}
		}
		rcu_stutter_wait("rcu_torture_fakewriter");
		stutter_wait("rcu_torture_fakewriter");
	} while (!torture_must_stop());
	} while (!torture_must_stop());


	VERBOSE_TOROUT_STRING("rcu_torture_fakewriter task stopping");
	VERBOSE_TOROUT_STRING("rcu_torture_fakewriter task stopping");
@@ -910,7 +895,7 @@ rcu_torture_reader(void *arg)
		preempt_enable();
		preempt_enable();
		cur_ops->readunlock(idx);
		cur_ops->readunlock(idx);
		schedule();
		schedule();
		rcu_stutter_wait("rcu_torture_reader");
		stutter_wait("rcu_torture_reader");
	} while (!torture_must_stop());
	} while (!torture_must_stop());
	VERBOSE_TOROUT_STRING("rcu_torture_reader task stopping");
	VERBOSE_TOROUT_STRING("rcu_torture_reader task stopping");
	torture_shutdown_absorb("rcu_torture_reader");
	torture_shutdown_absorb("rcu_torture_reader");
@@ -1034,25 +1019,6 @@ rcu_torture_stats(void *arg)
	return 0;
	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
static inline void
rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
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_barrier_cleanup();
	rcu_torture_stall_cleanup();
	rcu_torture_stall_cleanup();
	if (stutter_task) {
	torture_stutter_cleanup();
		VERBOSE_TOROUT_STRING("Stopping rcu_torture_stutter task");
		kthread_stop(stutter_task);
	}
	stutter_task = NULL;


	if (writer_task) {
	if (writer_task) {
		VERBOSE_TOROUT_STRING("Stopping rcu_torture_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,
		&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. */
	/* Process args and tell the world that the torturer is on the job. */
	for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
	for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
@@ -1682,21 +1644,14 @@ rcu_torture_init(void)
	if (stutter < 0)
	if (stutter < 0)
		stutter = 0;
		stutter = 0;
	if (stutter) {
	if (stutter) {
		/* Create the stutter thread */
		firsterr = torture_stutter_init(stutter * HZ);
		stutter_task = kthread_run(rcu_torture_stutter, NULL,
		if (firsterr)
					  "rcu_torture_stutter");
		if (IS_ERR(stutter_task)) {
			firsterr = PTR_ERR(stutter_task);
			VERBOSE_TOROUT_ERRSTRING("Failed to create stutter");
			stutter_task = NULL;
			goto unwind;
			goto unwind;
	}
	}
		torture_shuffle_task_register(stutter_task);
	}
	if (fqs_duration < 0)
	if (fqs_duration < 0)
		fqs_duration = 0;
		fqs_duration = 0;
	if (fqs_duration) {
	if (fqs_duration) {
		/* Create the stutter thread */
		/* Create the fqs thread */
		fqs_task = kthread_run(rcu_torture_fqs, NULL,
		fqs_task = kthread_run(rcu_torture_fqs, NULL,
				       "rcu_torture_fqs");
				       "rcu_torture_fqs");
		if (IS_ERR(fqs_task)) {
		if (IS_ERR(fqs_task)) {
+86 −1
Original line number Original line Diff line number Diff line
@@ -58,6 +58,7 @@ static bool verbose;
#define FULLSTOP_RMMOD    2	/* Normal rmmod of torture. */
#define FULLSTOP_RMMOD    2	/* Normal rmmod of torture. */
static int fullstop = FULLSTOP_RMMOD;
static int fullstop = FULLSTOP_RMMOD;
static DEFINE_MUTEX(fullstop_mutex);
static DEFINE_MUTEX(fullstop_mutex);
static int *torture_runnable;


#ifdef CONFIG_HOTPLUG_CPU
#ifdef CONFIG_HOTPLUG_CPU


@@ -452,17 +453,101 @@ static struct notifier_block torture_shutdown_nb = {
	.notifier_call = torture_shutdown_notify,
	.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
 * Initialize torture module.  Please note that this is -not- invoked via
 * the usual module_init() mechanism, but rather by an explicit call from
 * the usual module_init() mechanism, but rather by an explicit call from
 * the client torture module.  This call must be paired with a later
 * the client torture module.  This call must be paired with a later
 * torture_init_end().
 * 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);
	mutex_lock(&fullstop_mutex);
	torture_type = ttype;
	torture_type = ttype;
	verbose = v;
	verbose = v;
	torture_runnable = runnable;
	fullstop = FULLSTOP_DONTSTOP;
	fullstop = FULLSTOP_DONTSTOP;


}
}