diff options
author | Ned Bass <[email protected]> | 2012-01-17 14:23:58 -0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2012-01-18 10:36:51 -0800 |
commit | cf5d23fa1e953db970b04f593f83d7ffa9e9e0df (patch) | |
tree | f1bf342092c3c097b8a28be2b67bd572e94f5424 /module/splat | |
parent | 966e5200d35a2c38cdb9e14bfaf698a10b31603b (diff) |
Add taskq contention splat test
Add a test designed to generate contention on the taskq spinlock by
using a large number of threads (100) to perform a large number (131072)
of trivial work items from a single queue. This simulates conditions
that may occur with the zio free taskq when a 1TB file is removed from a
ZFS filesystem, for example. This test should always pass. Its purpose
is to provide a benchmark to easily measure the effectiveness of taskq
optimizations using statistics from the kernel lock profiler.
Signed-off-by: Brian Behlendorf <[email protected]>
Issue #32
Diffstat (limited to 'module/splat')
-rw-r--r-- | module/splat/splat-taskq.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/module/splat/splat-taskq.c b/module/splat/splat-taskq.c index b89b495f1..73142f9b6 100644 --- a/module/splat/splat-taskq.c +++ b/module/splat/splat-taskq.c @@ -57,9 +57,14 @@ #define SPLAT_TASKQ_TEST7_NAME "recurse" #define SPLAT_TASKQ_TEST7_DESC "Single task queue, recursive dispatch" +#define SPLAT_TASKQ_TEST8_ID 0x0208 +#define SPLAT_TASKQ_TEST8_NAME "contention" +#define SPLAT_TASKQ_TEST8_DESC "1 queue, 100 threads, 131072 tasks" + #define SPLAT_TASKQ_ORDER_MAX 8 #define SPLAT_TASKQ_DEPTH_MAX 16 + typedef struct splat_taskq_arg { int flag; int id; @@ -990,6 +995,113 @@ splat_taskq_test7(struct file *file, void *arg) return rc; } +/* + * Create a taskq with 100 threads and dispatch a huge number of trivial + * tasks to generate contention on tq->tq_lock. This test should always + * pass. The purpose is to provide a benchmark for measuring the + * effectiveness of taskq optimizations. + */ +static void +splat_taskq_test8_func(void *arg) +{ + splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; + ASSERT(tq_arg); + + atomic_inc(&tq_arg->count); +} + +#define TEST8_NUM_TASKS 0x20000 +#define TEST8_THREADS_PER_TASKQ 100 + +static int +splat_taskq_test8_common(struct file *file, void *arg, int minalloc, + int maxalloc) +{ + taskq_t *tq; + taskqid_t id; + splat_taskq_arg_t tq_arg; + taskq_ent_t **tqes; + int i, j, rc = 0; + + tqes = vmalloc(sizeof(*tqes) * TEST8_NUM_TASKS); + if (tqes == NULL) + return -ENOMEM; + memset(tqes, 0, sizeof(*tqes) * TEST8_NUM_TASKS); + + splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, + "Taskq '%s' creating (%d/%d/%d)\n", + SPLAT_TASKQ_TEST8_NAME, + minalloc, maxalloc, TEST8_NUM_TASKS); + if ((tq = taskq_create(SPLAT_TASKQ_TEST8_NAME, TEST8_THREADS_PER_TASKQ, + maxclsyspri, minalloc, maxalloc, + TASKQ_PREPOPULATE)) == NULL) { + splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, + "Taskq '%s' create failed\n", + SPLAT_TASKQ_TEST8_NAME); + rc = -EINVAL; + goto out_free; + } + + tq_arg.file = file; + tq_arg.name = SPLAT_TASKQ_TEST8_NAME; + + atomic_set(&tq_arg.count, 0); + for (i = 0; i < TEST8_NUM_TASKS; i++) { + tqes[i] = kmalloc(sizeof(taskq_ent_t), GFP_KERNEL); + if (tqes[i] == NULL) { + rc = -ENOMEM; + goto out; + } + taskq_init_ent(tqes[i]); + + taskq_dispatch_ent(tq, splat_taskq_test8_func, + &tq_arg, TQ_SLEEP, tqes[i]); + + id = tqes[i]->tqent_id; + + if (id == 0) { + splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, + "Taskq '%s' function '%s' dispatch " + "%d failed\n", tq_arg.name, + sym2str(splat_taskq_test8_func), i); + rc = -EINVAL; + goto out; + } + } + + splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' " + "waiting for %d dispatches\n", tq_arg.name, + TEST8_NUM_TASKS); + taskq_wait(tq); + splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' " + "%d/%d dispatches finished\n", tq_arg.name, + atomic_read(&tq_arg.count), TEST8_NUM_TASKS); + + if (atomic_read(&tq_arg.count) != TEST8_NUM_TASKS) + rc = -ERANGE; + +out: + splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' destroying\n", + tq_arg.name); + taskq_destroy(tq); +out_free: + for (j = 0; j < TEST8_NUM_TASKS && tqes[j] != NULL; j++) + kfree(tqes[j]); + vfree(tqes); + + return rc; +} + +static int +splat_taskq_test8(struct file *file, void *arg) +{ + int rc; + + rc = splat_taskq_test8_common(file, arg, 1, 100); + + return rc; +} + splat_subsystem_t * splat_taskq_init(void) { @@ -1021,6 +1133,8 @@ splat_taskq_init(void) SPLAT_TASKQ_TEST6_ID, splat_taskq_test6); SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST7_NAME, SPLAT_TASKQ_TEST7_DESC, SPLAT_TASKQ_TEST7_ID, splat_taskq_test7); + SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST8_NAME, SPLAT_TASKQ_TEST8_DESC, + SPLAT_TASKQ_TEST8_ID, splat_taskq_test8); return sub; } @@ -1029,6 +1143,7 @@ void splat_taskq_fini(splat_subsystem_t *sub) { ASSERT(sub); + SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST8_ID); SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST7_ID); SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST6_ID); SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST5_ID); |