diff options
| author | Ingo Molnar <mingo@elte.hu> | 2008-07-15 21:10:12 +0200 | 
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-07-15 21:10:12 +0200 | 
| commit | 6c9fcaf2eec1b9f85226a694230dd957dd7926b3 (patch) | |
| tree | f8c824c6c64dc411752c844f116e693760768bcc /kernel/rcutorture.c | |
| parent | b9d2252c1e44fa83a4e65fdc9eb93db6297c55af (diff) | |
| parent | 199a952876adbfc2b6c13b8b07adabebf4ff54b2 (diff) | |
Merge branch 'core/rcu' into core/rcu-for-linus
Diffstat (limited to 'kernel/rcutorture.c')
| -rw-r--r-- | kernel/rcutorture.c | 174 | 
1 files changed, 166 insertions, 8 deletions
| diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 33acc424667..90b5b123f7a 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -57,7 +57,9 @@ static int stat_interval;	/* Interval between stats, in seconds. */  				/*  Defaults to "only at end of test". */  static int verbose;		/* Print more debug info. */  static int test_no_idle_hz;	/* Test RCU's support for tickless idle CPUs. */ -static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/ +static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/ +static int stutter = 5;		/* Start/stop testing interval (in sec) */ +static int irqreader = 1;	/* RCU readers from irq (timers). */  static char *torture_type = "rcu"; /* What RCU implementation to torture. */  module_param(nreaders, int, 0444); @@ -72,6 +74,10 @@ module_param(test_no_idle_hz, bool, 0444);  MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs");  module_param(shuffle_interval, int, 0444);  MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); +module_param(stutter, int, 0444); +MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test"); +module_param(irqreader, int, 0444); +MODULE_PARM_DESC(irqreader, "Allow RCU readers from irq handlers");  module_param(torture_type, charp, 0444);  MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)"); @@ -91,6 +97,7 @@ static struct task_struct **fakewriter_tasks;  static struct task_struct **reader_tasks;  static struct task_struct *stats_task;  static struct task_struct *shuffler_task; +static struct task_struct *stutter_task;  #define RCU_TORTURE_PIPE_LEN 10 @@ -117,8 +124,18 @@ static atomic_t n_rcu_torture_alloc_fail;  static atomic_t n_rcu_torture_free;  static atomic_t n_rcu_torture_mberror;  static atomic_t n_rcu_torture_error; +static long n_rcu_torture_timers = 0;  static struct list_head rcu_torture_removed; +static int stutter_pause_test = 0; + +#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE) +#define RCUTORTURE_RUNNABLE_INIT 1 +#else +#define RCUTORTURE_RUNNABLE_INIT 0 +#endif +int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT; +  /*   * Allocate an element from the rcu_tortures pool.   */ @@ -179,6 +196,16 @@ rcu_random(struct rcu_random_state *rrsp)  	return swahw32(rrsp->rrs_state);  } +static void +rcu_stutter_wait(void) +{ +	while (stutter_pause_test || !rcutorture_runnable) +		if (rcutorture_runnable) +			schedule_timeout_interruptible(1); +		else +			schedule_timeout_interruptible(round_jiffies_relative(HZ)); +} +  /*   * Operations vector for selecting different types of tests.   */ @@ -192,7 +219,9 @@ struct rcu_torture_ops {  	int (*completed)(void);  	void (*deferredfree)(struct rcu_torture *p);  	void (*sync)(void); +	void (*cb_barrier)(void);  	int (*stats)(char *page); +	int irqcapable;  	char *name;  };  static struct rcu_torture_ops *cur_ops = NULL; @@ -265,7 +294,9 @@ static struct rcu_torture_ops rcu_ops = {  	.completed = rcu_torture_completed,  	.deferredfree = rcu_torture_deferred_free,  	.sync = synchronize_rcu, +	.cb_barrier = rcu_barrier,  	.stats = NULL, +	.irqcapable = 1,  	.name = "rcu"  }; @@ -304,7 +335,9 @@ static struct rcu_torture_ops rcu_sync_ops = {  	.completed = rcu_torture_completed,  	.deferredfree = rcu_sync_torture_deferred_free,  	.sync = synchronize_rcu, +	.cb_barrier = NULL,  	.stats = NULL, +	.irqcapable = 1,  	.name = "rcu_sync"  }; @@ -364,7 +397,9 @@ static struct rcu_torture_ops rcu_bh_ops = {  	.completed = rcu_bh_torture_completed,  	.deferredfree = rcu_bh_torture_deferred_free,  	.sync = rcu_bh_torture_synchronize, +	.cb_barrier = rcu_barrier_bh,  	.stats = NULL, +	.irqcapable = 1,  	.name = "rcu_bh"  }; @@ -377,7 +412,9 @@ static struct rcu_torture_ops rcu_bh_sync_ops = {  	.completed = rcu_bh_torture_completed,  	.deferredfree = rcu_sync_torture_deferred_free,  	.sync = rcu_bh_torture_synchronize, +	.cb_barrier = NULL,  	.stats = NULL, +	.irqcapable = 1,  	.name = "rcu_bh_sync"  }; @@ -458,6 +495,7 @@ static struct rcu_torture_ops srcu_ops = {  	.completed = srcu_torture_completed,  	.deferredfree = rcu_sync_torture_deferred_free,  	.sync = srcu_torture_synchronize, +	.cb_barrier = NULL,  	.stats = srcu_torture_stats,  	.name = "srcu"  }; @@ -482,6 +520,11 @@ static int sched_torture_completed(void)  	return 0;  } +static void rcu_sched_torture_deferred_free(struct rcu_torture *p) +{ +	call_rcu_sched(&p->rtort_rcu, rcu_torture_cb); +} +  static void sched_torture_synchronize(void)  {  	synchronize_sched(); @@ -494,12 +537,28 @@ static struct rcu_torture_ops sched_ops = {  	.readdelay = rcu_read_delay,  /* just reuse rcu's version. */  	.readunlock = sched_torture_read_unlock,  	.completed = sched_torture_completed, -	.deferredfree = rcu_sync_torture_deferred_free, +	.deferredfree = rcu_sched_torture_deferred_free,  	.sync = sched_torture_synchronize, +	.cb_barrier = rcu_barrier_sched,  	.stats = NULL, +	.irqcapable = 1,  	.name = "sched"  }; +static struct rcu_torture_ops sched_ops_sync = { +	.init = rcu_sync_torture_init, +	.cleanup = NULL, +	.readlock = sched_torture_read_lock, +	.readdelay = rcu_read_delay,  /* just reuse rcu's version. */ +	.readunlock = sched_torture_read_unlock, +	.completed = sched_torture_completed, +	.deferredfree = rcu_sync_torture_deferred_free, +	.sync = sched_torture_synchronize, +	.cb_barrier = NULL, +	.stats = NULL, +	.name = "sched_sync" +}; +  /*   * RCU torture writer kthread.  Repeatedly substitutes a new structure   * for that pointed to by rcu_torture_current, freeing the old structure @@ -537,6 +596,7 @@ rcu_torture_writer(void *arg)  		}  		rcu_torture_current_version++;  		oldbatch = cur_ops->completed(); +		rcu_stutter_wait();  	} while (!kthread_should_stop() && !fullstop);  	VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping");  	while (!kthread_should_stop()) @@ -560,6 +620,7 @@ rcu_torture_fakewriter(void *arg)  		schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10);  		udelay(rcu_random(&rand) & 0x3ff);  		cur_ops->sync(); +		rcu_stutter_wait();  	} while (!kthread_should_stop() && !fullstop);  	VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping"); @@ -569,6 +630,52 @@ rcu_torture_fakewriter(void *arg)  }  /* + * RCU torture reader from timer handler.  Dereferences rcu_torture_current, + * incrementing the corresponding element of the pipeline array.  The + * counter in the element should never be greater than 1, otherwise, the + * RCU implementation is broken. + */ +static void rcu_torture_timer(unsigned long unused) +{ +	int idx; +	int completed; +	static DEFINE_RCU_RANDOM(rand); +	static DEFINE_SPINLOCK(rand_lock); +	struct rcu_torture *p; +	int pipe_count; + +	idx = cur_ops->readlock(); +	completed = cur_ops->completed(); +	p = rcu_dereference(rcu_torture_current); +	if (p == NULL) { +		/* Leave because rcu_torture_writer is not yet underway */ +		cur_ops->readunlock(idx); +		return; +	} +	if (p->rtort_mbtest == 0) +		atomic_inc(&n_rcu_torture_mberror); +	spin_lock(&rand_lock); +	cur_ops->readdelay(&rand); +	n_rcu_torture_timers++; +	spin_unlock(&rand_lock); +	preempt_disable(); +	pipe_count = p->rtort_pipe_count; +	if (pipe_count > RCU_TORTURE_PIPE_LEN) { +		/* Should not happen, but... */ +		pipe_count = RCU_TORTURE_PIPE_LEN; +	} +	++__get_cpu_var(rcu_torture_count)[pipe_count]; +	completed = cur_ops->completed() - completed; +	if (completed > RCU_TORTURE_PIPE_LEN) { +		/* Should not happen, but... */ +		completed = RCU_TORTURE_PIPE_LEN; +	} +	++__get_cpu_var(rcu_torture_batch)[completed]; +	preempt_enable(); +	cur_ops->readunlock(idx); +} + +/*   * RCU torture reader kthread.  Repeatedly dereferences rcu_torture_current,   * incrementing the corresponding element of the pipeline array.  The   * counter in the element should never be greater than 1, otherwise, the @@ -582,11 +689,18 @@ rcu_torture_reader(void *arg)  	DEFINE_RCU_RANDOM(rand);  	struct rcu_torture *p;  	int pipe_count; +	struct timer_list t;  	VERBOSE_PRINTK_STRING("rcu_torture_reader task started");  	set_user_nice(current, 19); +	if (irqreader && cur_ops->irqcapable) +		setup_timer_on_stack(&t, rcu_torture_timer, 0);  	do { +		if (irqreader && cur_ops->irqcapable) { +			if (!timer_pending(&t)) +				mod_timer(&t, 1); +		}  		idx = cur_ops->readlock();  		completed = cur_ops->completed();  		p = rcu_dereference(rcu_torture_current); @@ -615,8 +729,11 @@ rcu_torture_reader(void *arg)  		preempt_enable();  		cur_ops->readunlock(idx);  		schedule(); +		rcu_stutter_wait();  	} while (!kthread_should_stop() && !fullstop);  	VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); +	if (irqreader && cur_ops->irqcapable) +		del_timer_sync(&t);  	while (!kthread_should_stop())  		schedule_timeout_uninterruptible(1);  	return 0; @@ -647,20 +764,22 @@ rcu_torture_printk(char *page)  	cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG);  	cnt += sprintf(&page[cnt],  		       "rtc: %p ver: %ld tfle: %d rta: %d rtaf: %d rtf: %d " -		       "rtmbe: %d", +		       "rtmbe: %d nt: %ld",  		       rcu_torture_current,  		       rcu_torture_current_version,  		       list_empty(&rcu_torture_freelist),  		       atomic_read(&n_rcu_torture_alloc),  		       atomic_read(&n_rcu_torture_alloc_fail),  		       atomic_read(&n_rcu_torture_free), -		       atomic_read(&n_rcu_torture_mberror)); +		       atomic_read(&n_rcu_torture_mberror), +		       n_rcu_torture_timers);  	if (atomic_read(&n_rcu_torture_mberror) != 0)  		cnt += sprintf(&page[cnt], " !!!");  	cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG);  	if (i > 1) {  		cnt += sprintf(&page[cnt], "!!! ");  		atomic_inc(&n_rcu_torture_error); +		WARN_ON_ONCE(1);  	}  	cnt += sprintf(&page[cnt], "Reader Pipe: ");  	for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) @@ -785,15 +904,34 @@ rcu_torture_shuffle(void *arg)  	return 0;  } +/* Cause the rcutorture test to "stutter", starting and stopping all + * threads periodically. + */ +static int +rcu_torture_stutter(void *arg) +{ +	VERBOSE_PRINTK_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; +	} while (!kthread_should_stop()); +	VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping"); +	return 0; +} +  static inline void  rcu_torture_print_module_parms(char *tag)  {  	printk(KERN_ALERT "%s" TORTURE_FLAG  		"--- %s: nreaders=%d nfakewriters=%d "  		"stat_interval=%d verbose=%d test_no_idle_hz=%d " -		"shuffle_interval = %d\n", +		"shuffle_interval=%d stutter=%d irqreader=%d\n",  		torture_type, tag, nrealreaders, nfakewriters, -		stat_interval, verbose, test_no_idle_hz, shuffle_interval); +		stat_interval, verbose, test_no_idle_hz, shuffle_interval, +		stutter, irqreader);  }  static void @@ -802,6 +940,11 @@ rcu_torture_cleanup(void)  	int i;  	fullstop = 1; +	if (stutter_task) { +		VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task"); +		kthread_stop(stutter_task); +	} +	stutter_task = NULL;  	if (shuffler_task) {  		VERBOSE_PRINTK_STRING("Stopping rcu_torture_shuffle task");  		kthread_stop(shuffler_task); @@ -848,7 +991,9 @@ rcu_torture_cleanup(void)  	stats_task = NULL;  	/* Wait for all RCU callbacks to fire.  */ -	rcu_barrier(); + +	if (cur_ops->cb_barrier != NULL) +		cur_ops->cb_barrier();  	rcu_torture_stats_print();  /* -After- the stats thread is stopped! */ @@ -868,7 +1013,7 @@ rcu_torture_init(void)  	int firsterr = 0;  	static struct rcu_torture_ops *torture_ops[] =  		{ &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops, -		  &srcu_ops, &sched_ops, }; +		  &srcu_ops, &sched_ops, &sched_ops_sync, };  	/* Process args and tell the world that the torturer is on the job. */  	for (i = 0; i < ARRAY_SIZE(torture_ops); i++) { @@ -988,6 +1133,19 @@ rcu_torture_init(void)  			goto unwind;  		}  	} +	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_PRINTK_ERRSTRING("Failed to create stutter"); +			stutter_task = NULL; +			goto unwind; +		} +	}  	return 0;  unwind: | 
