Skip to content

Commit

Permalink
[PATCH] RCU: add fake writers to rcutorture
Browse files Browse the repository at this point in the history
rcutorture currently has one writer and an arbitrary number of readers.  To
better exercise some of the code paths in RCU implementations, add fake
writer threads which call the synchronize function for the RCU variant in a
loop, with a delay between calls to arrange for different numbers of
writers running in parallel.

[[email protected]: cleanup]
Acked-by: Paul McKenney <[email protected]>
Cc: Dipkanar Sarma <[email protected]>
Signed-off-by: Josh Triplett <[email protected]>
Signed-off-by: Adrian Bunk <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Josh Triplett authored and Linus Torvalds committed Oct 4, 2006
1 parent 75cfef3 commit b772e1d
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 5 deletions.
9 changes: 9 additions & 0 deletions Documentation/RCU/torture.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ nreaders This is the number of RCU reading threads supported.
To properly exercise RCU implementations with preemptible
read-side critical sections.

nfakewriters This is the number of RCU fake writer threads to run. Fake
writer threads repeatedly use the synchronous "wait for
current readers" function of the interface selected by
torture_type, with a delay between calls to allow for various
different numbers of writers running in parallel.
nfakewriters defaults to 4, which provides enough parallelism
to trigger special cases caused by multiple writers, such as
the synchronize_srcu() early return optimization.

stat_interval The number of seconds between output of torture
statistics (via printk()). Regardless of the interval,
statistics are printed when the module is unloaded.
Expand Down
109 changes: 104 additions & 5 deletions kernel/rcutorture.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) IBM Corporation, 2005
* Copyright (C) IBM Corporation, 2005, 2006
*
* Authors: Paul E. McKenney <[email protected]>
* Josh Triplett <[email protected]>
*
* See also: Documentation/RCU/torture.txt
*/
Expand Down Expand Up @@ -47,9 +48,11 @@
#include <linux/srcu.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Paul E. McKenney <[email protected]>");
MODULE_AUTHOR("Paul E. McKenney <[email protected]> and "
"Josh Triplett <[email protected]>");

static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */
static int nfakewriters = 4; /* # fake writer threads */
static int stat_interval; /* Interval between stats, in seconds. */
/* Defaults to "only at end of test". */
static int verbose; /* Print more debug info. */
Expand All @@ -59,6 +62,8 @@ static char *torture_type = "rcu"; /* What to torture: rcu, rcu_bh, srcu. */

module_param(nreaders, int, 0);
MODULE_PARM_DESC(nreaders, "Number of RCU reader threads");
module_param(nfakewriters, int, 0);
MODULE_PARM_DESC(nfakewriters, "Number of RCU fake writer threads");
module_param(stat_interval, int, 0);
MODULE_PARM_DESC(stat_interval, "Number of seconds between stats printk()s");
module_param(verbose, bool, 0);
Expand All @@ -82,6 +87,7 @@ static char printk_buf[4096];

static int nrealreaders;
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 *shuffler_task;
Expand Down Expand Up @@ -186,6 +192,7 @@ struct rcu_torture_ops {
void (*readunlock)(int idx);
int (*completed)(void);
void (*deferredfree)(struct rcu_torture *p);
void (*sync)(void);
int (*stats)(char *page);
char *name;
};
Expand Down Expand Up @@ -258,6 +265,7 @@ static struct rcu_torture_ops rcu_ops = {
.readunlock = rcu_torture_read_unlock,
.completed = rcu_torture_completed,
.deferredfree = rcu_torture_deferred_free,
.sync = synchronize_rcu,
.stats = NULL,
.name = "rcu"
};
Expand Down Expand Up @@ -287,6 +295,28 @@ static void rcu_bh_torture_deferred_free(struct rcu_torture *p)
call_rcu_bh(&p->rtort_rcu, rcu_torture_cb);
}

struct rcu_bh_torture_synchronize {
struct rcu_head head;
struct completion completion;
};

static void rcu_bh_torture_wakeme_after_cb(struct rcu_head *head)
{
struct rcu_bh_torture_synchronize *rcu;

rcu = container_of(head, struct rcu_bh_torture_synchronize, head);
complete(&rcu->completion);
}

static void rcu_bh_torture_synchronize(void)
{
struct rcu_bh_torture_synchronize rcu;

init_completion(&rcu.completion);
call_rcu_bh(&rcu.head, rcu_bh_torture_wakeme_after_cb);
wait_for_completion(&rcu.completion);
}

static struct rcu_torture_ops rcu_bh_ops = {
.init = NULL,
.cleanup = NULL,
Expand All @@ -295,6 +325,7 @@ static struct rcu_torture_ops rcu_bh_ops = {
.readunlock = rcu_bh_torture_read_unlock,
.completed = rcu_bh_torture_completed,
.deferredfree = rcu_bh_torture_deferred_free,
.sync = rcu_bh_torture_synchronize,
.stats = NULL,
.name = "rcu_bh"
};
Expand Down Expand Up @@ -367,6 +398,11 @@ static void srcu_torture_deferred_free(struct rcu_torture *p)
}
}

static void srcu_torture_synchronize(void)
{
synchronize_srcu(&srcu_ctl);
}

static int srcu_torture_stats(char *page)
{
int cnt = 0;
Expand All @@ -392,6 +428,7 @@ static struct rcu_torture_ops srcu_ops = {
.readunlock = srcu_torture_read_unlock,
.completed = srcu_torture_completed,
.deferredfree = srcu_torture_deferred_free,
.sync = srcu_torture_synchronize,
.stats = srcu_torture_stats,
.name = "srcu"
};
Expand Down Expand Up @@ -443,6 +480,30 @@ rcu_torture_writer(void *arg)
return 0;
}

/*
* RCU torture fake writer kthread. Repeatedly calls sync, with a random
* delay between calls.
*/
static int
rcu_torture_fakewriter(void *arg)
{
DEFINE_RCU_RANDOM(rand);

VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task started");
set_user_nice(current, 19);

do {
schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10);
udelay(rcu_random(&rand) & 0x3ff);
cur_ops->sync();
} while (!kthread_should_stop() && !fullstop);

VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping");
while (!kthread_should_stop())
schedule_timeout_uninterruptible(1);
return 0;
}

/*
* RCU torture reader kthread. Repeatedly dereferences rcu_torture_current,
* incrementing the corresponding element of the pipeline array. The
Expand Down Expand Up @@ -621,6 +682,12 @@ static void rcu_torture_shuffle_tasks(void)
set_cpus_allowed(reader_tasks[i], tmp_mask);
}

if (fakewriter_tasks != NULL) {
for (i = 0; i < nfakewriters; i++)
if (fakewriter_tasks[i])
set_cpus_allowed(fakewriter_tasks[i], tmp_mask);
}

if (writer_task)
set_cpus_allowed(writer_task, tmp_mask);

Expand Down Expand Up @@ -654,11 +721,12 @@ rcu_torture_shuffle(void *arg)
static inline void
rcu_torture_print_module_parms(char *tag)
{
printk(KERN_ALERT "%s" TORTURE_FLAG "--- %s: nreaders=%d "
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",
torture_type, tag, nrealreaders, stat_interval, verbose,
test_no_idle_hz, shuffle_interval);
torture_type, tag, nrealreaders, nfakewriters,
stat_interval, verbose, test_no_idle_hz, shuffle_interval);
}

static void
Expand Down Expand Up @@ -693,6 +761,19 @@ rcu_torture_cleanup(void)
}
rcu_torture_current = NULL;

if (fakewriter_tasks != NULL) {
for (i = 0; i < nfakewriters; i++) {
if (fakewriter_tasks[i] != NULL) {
VERBOSE_PRINTK_STRING(
"Stopping rcu_torture_fakewriter task");
kthread_stop(fakewriter_tasks[i]);
}
fakewriter_tasks[i] = NULL;
}
kfree(fakewriter_tasks);
fakewriter_tasks = NULL;
}

if (stats_task != NULL) {
VERBOSE_PRINTK_STRING("Stopping rcu_torture_stats task");
kthread_stop(stats_task);
Expand Down Expand Up @@ -780,6 +861,24 @@ rcu_torture_init(void)
writer_task = NULL;
goto unwind;
}
fakewriter_tasks = kzalloc(nfakewriters * sizeof(fakewriter_tasks[0]),
GFP_KERNEL);
if (fakewriter_tasks == NULL) {
VERBOSE_PRINTK_ERRSTRING("out of memory");
firsterr = -ENOMEM;
goto unwind;
}
for (i = 0; i < nfakewriters; i++) {
VERBOSE_PRINTK_STRING("Creating rcu_torture_fakewriter task");
fakewriter_tasks[i] = kthread_run(rcu_torture_fakewriter, NULL,
"rcu_torture_fakewriter");
if (IS_ERR(fakewriter_tasks[i])) {
firsterr = PTR_ERR(fakewriter_tasks[i]);
VERBOSE_PRINTK_ERRSTRING("Failed to create fakewriter");
fakewriter_tasks[i] = NULL;
goto unwind;
}
}
reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]),
GFP_KERNEL);
if (reader_tasks == NULL) {
Expand Down

0 comments on commit b772e1d

Please sign in to comment.