From 55f10ae5e91817e339f89e312a747ef6bc2dde66 Mon Sep 17 00:00:00 2001 From: Ned Bass Date: Wed, 30 Jun 2010 17:34:57 -0700 Subject: [PATCH] Implementation of a regression test for TQ_FRONT. Use 3 threads and 8 tasks. Dispatch the final 3 tasks with TQ_FRONT. The first three tasks keep the worker threads busy while we stuff the queues. Use msleep() to force a known execution order, assuming TQ_FRONT is properly honored. Verify that the expected completion order occurs. The splat_taskq_test5_order() function may be useful in more than one test. This commit generalizes it by renaming the function to splat_taskq_test_order() and adding a name argument instead of assuming SPLAT_TASKQ_TEST5_NAME as the test name. The documentation for splat taskq regression test #5 swaps the two required completion orders in the diagram. This commit corrects the error. Signed-off-by: Brian Behlendorf --- module/splat/splat-taskq.c | 151 ++++++++++++++++++++++++++++++++++--- 1 file changed, 142 insertions(+), 9 deletions(-) diff --git a/module/splat/splat-taskq.c b/module/splat/splat-taskq.c index ea79dfa..d4540f3 100644 --- a/module/splat/splat-taskq.c +++ b/module/splat/splat-taskq.c @@ -49,6 +49,10 @@ #define SPLAT_TASKQ_TEST5_NAME "order" #define SPLAT_TASKQ_TEST5_DESC "Correct task ordering" +#define SPLAT_TASKQ_TEST6_ID 0x0206 +#define SPLAT_TASKQ_TEST6_NAME "front" +#define SPLAT_TASKQ_TEST6_DESC "Correct ordering with TQ_FRONT flag" + #define SPLAT_TASKQ_ORDER_MAX 8 typedef struct splat_taskq_arg { @@ -386,13 +390,13 @@ out: * task ids which must have completed and their order. * * +-----+ <--- taskq_wait_id(tq, 8) unblocks - * | | Required Completion Order: 1,2,4,5,3 + * | | Required Completion Order: 1,2,4,5,3,8,6,7 * +-----+ | * | | | * | | +-----+ * | | | 8 | * | | +-----+ <--- taskq_wait_id(tq, 3) unblocks - * | | 7 | | Required Completion Order: 1,2,4,5,3,8,6,7 + * | | 7 | | Required Completion Order: 1,2,4,5,3 * | +-----+ | * | 6 | | | * +-----+ | | @@ -432,23 +436,23 @@ splat_taskq_test5_func(void *arg) } static int -splat_taskq_test5_order(splat_taskq_arg_t *tq_arg, int *order) +splat_taskq_test_order(splat_taskq_arg_t *tq_arg, int *order) { int i, j; for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) { if (tq_arg->order[i] != order[i]) { - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST5_NAME, + splat_vprint(tq_arg->file, tq_arg->name, "Taskq '%s' incorrect completion " "order\n", tq_arg->name); - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST5_NAME, + splat_vprint(tq_arg->file, tq_arg->name, "%s", "Expected { "); for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++) splat_print(tq_arg->file, "%d ", order[j]); splat_print(tq_arg->file, "%s", "}\n"); - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST5_NAME, + splat_vprint(tq_arg->file, tq_arg->name, "%s", "Got { "); for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++) @@ -460,7 +464,7 @@ splat_taskq_test5_order(splat_taskq_arg_t *tq_arg, int *order) } } - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST5_NAME, + splat_vprint(tq_arg->file, tq_arg->name, "Taskq '%s' validated correct completion order\n", tq_arg->name); @@ -519,13 +523,13 @@ splat_taskq_test5(struct file *file, void *arg) splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' " "waiting for taskqid %d completion\n", tq_arg.name, 3); taskq_wait_id(tq, 3); - if ((rc = splat_taskq_test5_order(&tq_arg, order1))) + if ((rc = splat_taskq_test_order(&tq_arg, order1))) goto out; splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' " "waiting for taskqid %d completion\n", tq_arg.name, 8); taskq_wait_id(tq, 8); - rc = splat_taskq_test5_order(&tq_arg, order2); + rc = splat_taskq_test_order(&tq_arg, order2); out: splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, @@ -535,6 +539,133 @@ out: return rc; } +/* + * Create a single task queue with three threads. Dispatch 8 tasks, + * setting TQ_FRONT on only the last three. Sleep after + * dispatching tasks 1-3 to ensure they will run and hold the threads + * busy while we dispatch the remaining tasks. Verify that tasks 6-8 + * run before task 4-5. + * + * The following table shows each task id and how they will be + * scheduled. Each rows represent one time unit and each column + * one of the three worker threads. + * + * +-----+ + * | | + * +-----+ | + * | | 5 +-----+ + * | | | | + * | +-----| | + * | 4 | | | + * +-----+ | 8 | + * | | | | + * | | 7 +-----+ + * | | | | + * | |-----+ | + * | 6 | | | + * +-----+ | | + * | | | | + * | 1 | 2 | 3 | + * +-----+-----+-----+ + * + */ +static void +splat_taskq_test6_func(void *arg) +{ + splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg; + splat_taskq_arg_t *tq_arg = tq_id->arg; + int factor; + + /* Delays determined by above table */ + switch (tq_id->id) { + default: factor = 0; break; + case 1: factor = 2; break; + case 2: case 4: case 5: factor = 4; break; + case 6: case 7: case 8: factor = 5; break; + case 3: factor = 6; break; + } + + msleep(factor * 100); + + splat_vprint(tq_arg->file, tq_arg->name, + "Taskqid %d complete for taskq '%s'\n", + tq_id->id, tq_arg->name); + + spin_lock(&tq_arg->lock); + tq_arg->order[tq_arg->flag] = tq_id->id; + tq_arg->flag++; + spin_unlock(&tq_arg->lock); +} + +static int +splat_taskq_test6(struct file *file, void *arg) +{ + taskq_t *tq; + taskqid_t id; + splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX]; + splat_taskq_arg_t tq_arg; + int order[SPLAT_TASKQ_ORDER_MAX] = { 1,2,3,6,7,8,4,5 }; + int i, rc = 0; + uint_t tflags; + + splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' creating\n", + SPLAT_TASKQ_TEST6_NAME); + if ((tq = taskq_create(SPLAT_TASKQ_TEST6_NAME, 3, maxclsyspri, + 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) { + splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, + "Taskq '%s' create failed\n", + SPLAT_TASKQ_TEST6_NAME); + return -EINVAL; + } + + tq_arg.flag = 0; + memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX); + spin_lock_init(&tq_arg.lock); + tq_arg.file = file; + tq_arg.name = SPLAT_TASKQ_TEST6_NAME; + + for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) { + tq_id[i].id = i + 1; + tq_id[i].arg = &tq_arg; + tflags = TQ_SLEEP; + if (i > 4) + tflags |= TQ_FRONT; + + if ((id = taskq_dispatch(tq, splat_taskq_test6_func, + &tq_id[i], tflags)) == 0) { + splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, + "Taskq '%s' function '%s' dispatch failed\n", + tq_arg.name, sym2str(splat_taskq_test6_func)); + rc = -EINVAL; + goto out; + } + + if (tq_id[i].id != id) { + splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, + "Taskq '%s' expected taskqid %d got %d\n", + tq_arg.name, (int)tq_id[i].id, (int)id); + rc = -EINVAL; + goto out; + } + /* Sleep to let tasks 1-3 start executing. */ + if ( i == 2 ) + msleep(100); + } + + splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' " + "waiting for taskqid %d completion\n", tq_arg.name, + SPLAT_TASKQ_ORDER_MAX); + taskq_wait_id(tq, SPLAT_TASKQ_ORDER_MAX); + rc = splat_taskq_test_order(&tq_arg, order); + +out: + splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, + "Taskq '%s' destroying\n", tq_arg.name); + taskq_destroy(tq); + + return rc; +} + splat_subsystem_t * splat_taskq_init(void) { @@ -562,6 +693,8 @@ splat_taskq_init(void) SPLAT_TASKQ_TEST4_ID, splat_taskq_test4); SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST5_NAME, SPLAT_TASKQ_TEST5_DESC, SPLAT_TASKQ_TEST5_ID, splat_taskq_test5); + SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST6_NAME, SPLAT_TASKQ_TEST6_DESC, + SPLAT_TASKQ_TEST6_ID, splat_taskq_test6); return sub; } -- 2.39.2