]> git.proxmox.com Git - mirror_spl.git/blame - module/splat/splat-taskq.c
Implement taskq_dispatch_prealloc() interface
[mirror_spl.git] / module / splat / splat-taskq.c
CommitLineData
716154c5
BB
1/*****************************************************************************\
2 * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
3 * Copyright (C) 2007 The Regents of the University of California.
4 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
5 * Written by Brian Behlendorf <behlendorf1@llnl.gov>.
715f6251 6 * UCRL-CODE-235197
7 *
716154c5
BB
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://github.com/behlendorf/spl/>.
10 *
11 * The SPL is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
715f6251 15 *
716154c5 16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
715f6251 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
716154c5
BB
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting LAyer Tests (SPLAT) Task Queue Tests.
25\*****************************************************************************/
715f6251 26
7c50328b 27#include "splat-internal.h"
f1ca4da6 28
7c50328b 29#define SPLAT_TASKQ_NAME "taskq"
30#define SPLAT_TASKQ_DESC "Kernel Task Queue Tests"
f1ca4da6 31
7c50328b 32#define SPLAT_TASKQ_TEST1_ID 0x0201
33#define SPLAT_TASKQ_TEST1_NAME "single"
34#define SPLAT_TASKQ_TEST1_DESC "Single task queue, single task"
f1ca4da6 35
5562e5d1 36#define SPLAT_TASKQ_TEST2_ID 0x0202
7c50328b 37#define SPLAT_TASKQ_TEST2_NAME "multiple"
38#define SPLAT_TASKQ_TEST2_DESC "Multiple task queues, multiple tasks"
f1ca4da6 39
5562e5d1 40#define SPLAT_TASKQ_TEST3_ID 0x0203
e9cb2b4f
BB
41#define SPLAT_TASKQ_TEST3_NAME "system"
42#define SPLAT_TASKQ_TEST3_DESC "System task queue, multiple tasks"
43
5562e5d1 44#define SPLAT_TASKQ_TEST4_ID 0x0204
7257ec41
BB
45#define SPLAT_TASKQ_TEST4_NAME "wait"
46#define SPLAT_TASKQ_TEST4_DESC "Multiple task waiting"
47
5562e5d1
BB
48#define SPLAT_TASKQ_TEST5_ID 0x0205
49#define SPLAT_TASKQ_TEST5_NAME "order"
50#define SPLAT_TASKQ_TEST5_DESC "Correct task ordering"
51
55f10ae5
NB
52#define SPLAT_TASKQ_TEST6_ID 0x0206
53#define SPLAT_TASKQ_TEST6_NAME "front"
54#define SPLAT_TASKQ_TEST6_DESC "Correct ordering with TQ_FRONT flag"
55
ac1e5b60
PS
56#define SPLAT_TASKQ_TEST7_ID 0x0207
57#define SPLAT_TASKQ_TEST7_NAME "recurse"
58#define SPLAT_TASKQ_TEST7_DESC "Single task queue, recursive dispatch"
59
5562e5d1 60#define SPLAT_TASKQ_ORDER_MAX 8
ac1e5b60 61#define SPLAT_TASKQ_DEPTH_MAX 16
5562e5d1 62
7c50328b 63typedef struct splat_taskq_arg {
f1ca4da6 64 int flag;
65 int id;
7257ec41 66 atomic_t count;
5562e5d1 67 int order[SPLAT_TASKQ_ORDER_MAX];
ac1e5b60
PS
68 unsigned int depth;
69 taskq_t *tq;
5562e5d1 70 spinlock_t lock;
f1ca4da6 71 struct file *file;
72 const char *name;
7c50328b 73} splat_taskq_arg_t;
f1ca4da6 74
5562e5d1
BB
75typedef struct splat_taskq_id {
76 int id;
77 splat_taskq_arg_t *arg;
78} splat_taskq_id_t;
79
80/*
81 * Create a taskq, queue a task, wait until task completes, ensure
82 * task ran properly, cleanup taskq.
f1ca4da6 83 */
84static void
e9cb2b4f 85splat_taskq_test13_func(void *arg)
f1ca4da6 86{
7c50328b 87 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
f1ca4da6 88
89 ASSERT(tq_arg);
7c50328b 90 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST1_NAME,
f1ca4da6 91 "Taskq '%s' function '%s' setting flag\n",
e9cb2b4f 92 tq_arg->name, sym2str(splat_taskq_test13_func));
f1ca4da6 93 tq_arg->flag = 1;
94}
95
96static int
7c50328b 97splat_taskq_test1(struct file *file, void *arg)
f1ca4da6 98{
99 taskq_t *tq;
100 taskqid_t id;
7c50328b 101 splat_taskq_arg_t tq_arg;
f1ca4da6 102
7c50328b 103 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' creating\n",
104 SPLAT_TASKQ_TEST1_NAME);
bcd68186 105 if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, maxclsyspri,
106 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
7c50328b 107 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
f1ca4da6 108 "Taskq '%s' create failed\n",
7c50328b 109 SPLAT_TASKQ_TEST1_NAME);
f1ca4da6 110 return -EINVAL;
111 }
112
113 tq_arg.flag = 0;
114 tq_arg.id = 0;
115 tq_arg.file = file;
7c50328b 116 tq_arg.name = SPLAT_TASKQ_TEST1_NAME;
f1ca4da6 117
7c50328b 118 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
f1ca4da6 119 "Taskq '%s' function '%s' dispatching\n",
e9cb2b4f
BB
120 tq_arg.name, sym2str(splat_taskq_test13_func));
121 if ((id = taskq_dispatch(tq, splat_taskq_test13_func,
bcd68186 122 &tq_arg, TQ_SLEEP)) == 0) {
7c50328b 123 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
f1ca4da6 124 "Taskq '%s' function '%s' dispatch failed\n",
e9cb2b4f 125 tq_arg.name, sym2str(splat_taskq_test13_func));
4098c921 126 taskq_destroy(tq);
f1ca4da6 127 return -EINVAL;
128 }
129
7c50328b 130 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
f1ca4da6 131 tq_arg.name);
132 taskq_wait(tq);
7c50328b 133 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
f1ca4da6 134 tq_arg.name);
4098c921 135 taskq_destroy(tq);
f1ca4da6 136
137 return (tq_arg.flag) ? 0 : -EINVAL;
138}
139
5562e5d1
BB
140/*
141 * Create multiple taskq's, each with multiple tasks, wait until
142 * all tasks complete, ensure all tasks ran properly and in the
143 * correct order. Run order must be the same as the order submitted
144 * because we only have 1 thread per taskq. Finally cleanup the taskq.
f1ca4da6 145 */
146static void
7c50328b 147splat_taskq_test2_func1(void *arg)
f1ca4da6 148{
7c50328b 149 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
f1ca4da6 150
151 ASSERT(tq_arg);
7c50328b 152 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 153 "Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
154 tq_arg->name, tq_arg->id,
7c50328b 155 sym2str(splat_taskq_test2_func1),
f1ca4da6 156 tq_arg->flag * 2, tq_arg->flag);
157 tq_arg->flag *= 2;
158}
159
160static void
7c50328b 161splat_taskq_test2_func2(void *arg)
f1ca4da6 162{
7c50328b 163 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
f1ca4da6 164
165 ASSERT(tq_arg);
7c50328b 166 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 167 "Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
168 tq_arg->name, tq_arg->id,
7c50328b 169 sym2str(splat_taskq_test2_func2),
f1ca4da6 170 tq_arg->flag + 1, tq_arg->flag);
171 tq_arg->flag += 1;
172}
173
174#define TEST2_TASKQS 8
5562e5d1 175#define TEST2_THREADS_PER_TASKQ 1
bcd68186 176
f1ca4da6 177static int
7c50328b 178splat_taskq_test2(struct file *file, void *arg) {
f1ca4da6 179 taskq_t *tq[TEST2_TASKQS] = { NULL };
180 taskqid_t id;
7c50328b 181 splat_taskq_arg_t tq_args[TEST2_TASKQS];
f1ca4da6 182 int i, rc = 0;
183
184 for (i = 0; i < TEST2_TASKQS; i++) {
185
7c50328b 186 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq '%s/%d' "
187 "creating\n", SPLAT_TASKQ_TEST2_NAME, i);
188 if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME,
bcd68186 189 TEST2_THREADS_PER_TASKQ,
190 maxclsyspri, 50, INT_MAX,
191 TASKQ_PREPOPULATE)) == NULL) {
7c50328b 192 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 193 "Taskq '%s/%d' create failed\n",
7c50328b 194 SPLAT_TASKQ_TEST2_NAME, i);
f1ca4da6 195 rc = -EINVAL;
196 break;
197 }
198
199 tq_args[i].flag = i;
200 tq_args[i].id = i;
201 tq_args[i].file = file;
7c50328b 202 tq_args[i].name = SPLAT_TASKQ_TEST2_NAME;
f1ca4da6 203
7c50328b 204 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 205 "Taskq '%s/%d' function '%s' dispatching\n",
7c50328b 206 tq_args[i].name, tq_args[i].id,
207 sym2str(splat_taskq_test2_func1));
f1ca4da6 208 if ((id = taskq_dispatch(
bcd68186 209 tq[i], splat_taskq_test2_func1,
210 &tq_args[i], TQ_SLEEP)) == 0) {
7c50328b 211 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 212 "Taskq '%s/%d' function '%s' dispatch "
213 "failed\n", tq_args[i].name, tq_args[i].id,
7c50328b 214 sym2str(splat_taskq_test2_func1));
f1ca4da6 215 rc = -EINVAL;
216 break;
217 }
218
7c50328b 219 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 220 "Taskq '%s/%d' function '%s' dispatching\n",
7c50328b 221 tq_args[i].name, tq_args[i].id,
222 sym2str(splat_taskq_test2_func2));
f1ca4da6 223 if ((id = taskq_dispatch(
bcd68186 224 tq[i], splat_taskq_test2_func2,
225 &tq_args[i], TQ_SLEEP)) == 0) {
7c50328b 226 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 227 "Taskq '%s/%d' function '%s' dispatch failed\n",
228 tq_args[i].name, tq_args[i].id,
7c50328b 229 sym2str(splat_taskq_test2_func2));
f1ca4da6 230 rc = -EINVAL;
231 break;
232 }
233 }
234
235 /* When rc is set we're effectively just doing cleanup here, so
236 * ignore new errors in that case. They just cause noise. */
237 for (i = 0; i < TEST2_TASKQS; i++) {
238 if (tq[i] != NULL) {
7c50328b 239 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 240 "Taskq '%s/%d' waiting\n",
241 tq_args[i].name, tq_args[i].id);
242 taskq_wait(tq[i]);
7c50328b 243 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 244 "Taskq '%s/%d; destroying\n",
245 tq_args[i].name, tq_args[i].id);
4098c921 246 taskq_destroy(tq[i]);
f1ca4da6 247
248 if (!rc && tq_args[i].flag != ((i * 2) + 1)) {
7c50328b 249 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 250 "Taskq '%s/%d' processed tasks "
251 "out of order; %d != %d\n",
252 tq_args[i].name, tq_args[i].id,
253 tq_args[i].flag, i * 2 + 1);
254 rc = -EINVAL;
255 } else {
7c50328b 256 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 257 "Taskq '%s/%d' processed tasks "
258 "in the correct order; %d == %d\n",
259 tq_args[i].name, tq_args[i].id,
260 tq_args[i].flag, i * 2 + 1);
261 }
262 }
263 }
264
265 return rc;
266}
267
5562e5d1
BB
268/*
269 * Use the global system task queue with a single task, wait until task
270 * completes, ensure task ran properly.
e9cb2b4f
BB
271 */
272static int
273splat_taskq_test3(struct file *file, void *arg)
274{
275 taskqid_t id;
276 splat_taskq_arg_t tq_arg;
277
278 tq_arg.flag = 0;
279 tq_arg.id = 0;
280 tq_arg.file = file;
281 tq_arg.name = SPLAT_TASKQ_TEST3_NAME;
282
283 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
284 "Taskq '%s' function '%s' dispatching\n",
285 tq_arg.name, sym2str(splat_taskq_test13_func));
286 if ((id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
287 &tq_arg, TQ_SLEEP)) == 0) {
288 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
289 "Taskq '%s' function '%s' dispatch failed\n",
290 tq_arg.name, sym2str(splat_taskq_test13_func));
291 return -EINVAL;
292 }
293
294 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
295 tq_arg.name);
296 taskq_wait(system_taskq);
297
298 return (tq_arg.flag) ? 0 : -EINVAL;
299}
300
5562e5d1
BB
301/*
302 * Create a taskq and dispatch a large number of tasks to the queue.
303 * Then use taskq_wait() to block until all the tasks complete, then
304 * cross check that all the tasks ran by checking tg_arg->count which
305 * is incremented in the task function. Finally cleanup the taskq.
26f7245c
RC
306 *
307 * First we try with a large 'maxalloc' value, then we try with a small one.
308 * We should not drop tasks when TQ_SLEEP is used in taskq_dispatch(), even
309 * if the number of pending tasks is above maxalloc.
5562e5d1 310 */
7257ec41
BB
311static void
312splat_taskq_test4_func(void *arg)
313{
314 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
315 ASSERT(tq_arg);
316
317 atomic_inc(&tq_arg->count);
318}
319
320static int
26f7245c
RC
321splat_taskq_test4_common(struct file *file, void *arg, int minalloc,
322 int maxalloc, int nr_tasks)
7257ec41
BB
323{
324 taskq_t *tq;
325 splat_taskq_arg_t tq_arg;
326 int i, j, rc = 0;
327
26f7245c
RC
328 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' creating "
329 "(%d/%d/%d)\n", SPLAT_TASKQ_TEST4_NAME, minalloc, maxalloc,
330 nr_tasks);
7257ec41 331 if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, maxclsyspri,
26f7245c 332 minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
7257ec41
BB
333 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
334 "Taskq '%s' create failed\n",
335 SPLAT_TASKQ_TEST4_NAME);
336 return -EINVAL;
337 }
338
339 tq_arg.file = file;
340 tq_arg.name = SPLAT_TASKQ_TEST4_NAME;
341
26f7245c 342 for (i = 1; i <= nr_tasks; i *= 2) {
7257ec41
BB
343 atomic_set(&tq_arg.count, 0);
344 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
345 "Taskq '%s' function '%s' dispatched %d times\n",
346 tq_arg.name, sym2str(splat_taskq_test4_func), i);
347
348 for (j = 0; j < i; j++) {
349 if ((taskq_dispatch(tq, splat_taskq_test4_func,
350 &tq_arg, TQ_SLEEP)) == 0) {
351 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
352 "Taskq '%s' function '%s' dispatch "
353 "%d failed\n", tq_arg.name,
e05bec80 354 sym2str(splat_taskq_test4_func), j);
7257ec41
BB
355 rc = -EINVAL;
356 goto out;
357 }
358 }
359
360 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
361 "waiting for %d dispatches\n", tq_arg.name, i);
362 taskq_wait(tq);
363 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
364 "%d/%d dispatches finished\n", tq_arg.name,
365 atomic_read(&tq_arg.count), i);
366 if (atomic_read(&tq_arg.count) != i) {
367 rc = -ERANGE;
368 goto out;
369
370 }
371 }
372out:
373 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' destroying\n",
374 tq_arg.name);
375 taskq_destroy(tq);
376
377 return rc;
378}
379
26f7245c
RC
380static int splat_taskq_test4(struct file *file, void *arg)
381{
382 int rc;
383
384 rc = splat_taskq_test4_common(file, arg, 50, INT_MAX, 1024);
385 if (rc)
386 return rc;
387
388 rc = splat_taskq_test4_common(file, arg, 1, 1, 32);
389
390 return rc;
391}
392
5562e5d1
BB
393/*
394 * Create a taskq and dispatch a specific sequence of tasks carefully
395 * crafted to validate the order in which tasks are processed. When
396 * there are multiple worker threads each thread will process the
397 * next pending task as soon as it completes its current task. This
398 * means that tasks do not strictly complete in order in which they
399 * were dispatched (increasing task id). This is fine but we need to
400 * verify that taskq_wait_id() blocks until the passed task id and all
401 * lower task ids complete. We do this by dispatching the following
402 * specific sequence of tasks each of which block for N time units.
403 * We then use taskq_wait_id() to unblock at specific task id and
404 * verify the only the expected task ids have completed and in the
405 * correct order. The two cases of interest are:
406 *
407 * 1) Task ids larger than the waited for task id can run and
408 * complete as long as there is an available worker thread.
409 * 2) All task ids lower than the waited one must complete before
410 * unblocking even if the waited task id itself has completed.
411 *
412 * The following table shows each task id and how they will be
413 * scheduled. Each rows represent one time unit and each column
414 * one of the three worker threads. The places taskq_wait_id()
415 * must unblock for a specific id are identified as well as the
416 * task ids which must have completed and their order.
417 *
418 * +-----+ <--- taskq_wait_id(tq, 8) unblocks
55f10ae5 419 * | | Required Completion Order: 1,2,4,5,3,8,6,7
5562e5d1
BB
420 * +-----+ |
421 * | | |
422 * | | +-----+
423 * | | | 8 |
424 * | | +-----+ <--- taskq_wait_id(tq, 3) unblocks
55f10ae5 425 * | | 7 | | Required Completion Order: 1,2,4,5,3
5562e5d1
BB
426 * | +-----+ |
427 * | 6 | | |
428 * +-----+ | |
429 * | | 5 | |
430 * | +-----+ |
431 * | 4 | | |
432 * +-----+ | |
433 * | 1 | 2 | 3 |
434 * +-----+-----+-----+
435 *
436 */
437static void
438splat_taskq_test5_func(void *arg)
439{
440 splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg;
441 splat_taskq_arg_t *tq_arg = tq_id->arg;
442 int factor;
443
444 /* Delays determined by above table */
445 switch (tq_id->id) {
446 default: factor = 0; break;
447 case 1: case 8: factor = 1; break;
448 case 2: case 4: case 5: factor = 2; break;
449 case 6: case 7: factor = 4; break;
450 case 3: factor = 5; break;
451 }
452
453 msleep(factor * 100);
454 splat_vprint(tq_arg->file, tq_arg->name,
455 "Taskqid %d complete for taskq '%s'\n",
456 tq_id->id, tq_arg->name);
457
458 spin_lock(&tq_arg->lock);
459 tq_arg->order[tq_arg->flag] = tq_id->id;
460 tq_arg->flag++;
461 spin_unlock(&tq_arg->lock);
462}
463
464static int
55f10ae5 465splat_taskq_test_order(splat_taskq_arg_t *tq_arg, int *order)
5562e5d1
BB
466{
467 int i, j;
468
469 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
470 if (tq_arg->order[i] != order[i]) {
55f10ae5 471 splat_vprint(tq_arg->file, tq_arg->name,
5562e5d1
BB
472 "Taskq '%s' incorrect completion "
473 "order\n", tq_arg->name);
55f10ae5 474 splat_vprint(tq_arg->file, tq_arg->name,
5562e5d1
BB
475 "%s", "Expected { ");
476
477 for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++)
478 splat_print(tq_arg->file, "%d ", order[j]);
479
480 splat_print(tq_arg->file, "%s", "}\n");
55f10ae5 481 splat_vprint(tq_arg->file, tq_arg->name,
5562e5d1
BB
482 "%s", "Got { ");
483
484 for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++)
485 splat_print(tq_arg->file, "%d ",
486 tq_arg->order[j]);
487
488 splat_print(tq_arg->file, "%s", "}\n");
489 return -EILSEQ;
490 }
491 }
492
55f10ae5 493 splat_vprint(tq_arg->file, tq_arg->name,
5562e5d1
BB
494 "Taskq '%s' validated correct completion order\n",
495 tq_arg->name);
496
497 return 0;
498}
499
500static int
501splat_taskq_test5(struct file *file, void *arg)
502{
503 taskq_t *tq;
504 taskqid_t id;
505 splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX];
506 splat_taskq_arg_t tq_arg;
507 int order1[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,0,0,0 };
508 int order2[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,8,6,7 };
509 int i, rc = 0;
510
511 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' creating\n",
512 SPLAT_TASKQ_TEST5_NAME);
513 if ((tq = taskq_create(SPLAT_TASKQ_TEST5_NAME, 3, maxclsyspri,
514 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
515 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
516 "Taskq '%s' create failed\n",
517 SPLAT_TASKQ_TEST5_NAME);
518 return -EINVAL;
519 }
520
521 tq_arg.flag = 0;
522 memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX);
523 spin_lock_init(&tq_arg.lock);
524 tq_arg.file = file;
525 tq_arg.name = SPLAT_TASKQ_TEST5_NAME;
526
527 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
528 tq_id[i].id = i + 1;
529 tq_id[i].arg = &tq_arg;
530
531 if ((id = taskq_dispatch(tq, splat_taskq_test5_func,
532 &tq_id[i], TQ_SLEEP)) == 0) {
533 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
534 "Taskq '%s' function '%s' dispatch failed\n",
535 tq_arg.name, sym2str(splat_taskq_test5_func));
536 rc = -EINVAL;
537 goto out;
538 }
539
540 if (tq_id[i].id != id) {
541 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
542 "Taskq '%s' expected taskqid %d got %d\n",
543 tq_arg.name, (int)tq_id[i].id, (int)id);
544 rc = -EINVAL;
545 goto out;
546 }
547 }
548
549 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' "
550 "waiting for taskqid %d completion\n", tq_arg.name, 3);
551 taskq_wait_id(tq, 3);
55f10ae5 552 if ((rc = splat_taskq_test_order(&tq_arg, order1)))
5562e5d1
BB
553 goto out;
554
555 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' "
556 "waiting for taskqid %d completion\n", tq_arg.name, 8);
557 taskq_wait_id(tq, 8);
55f10ae5 558 rc = splat_taskq_test_order(&tq_arg, order2);
5562e5d1
BB
559
560out:
561 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
562 "Taskq '%s' destroying\n", tq_arg.name);
563 taskq_destroy(tq);
564
565 return rc;
566}
567
55f10ae5
NB
568/*
569 * Create a single task queue with three threads. Dispatch 8 tasks,
570 * setting TQ_FRONT on only the last three. Sleep after
571 * dispatching tasks 1-3 to ensure they will run and hold the threads
572 * busy while we dispatch the remaining tasks. Verify that tasks 6-8
573 * run before task 4-5.
574 *
575 * The following table shows each task id and how they will be
576 * scheduled. Each rows represent one time unit and each column
577 * one of the three worker threads.
578 *
579 * +-----+
580 * | |
581 * +-----+ |
582 * | | 5 +-----+
583 * | | | |
584 * | +-----| |
585 * | 4 | | |
586 * +-----+ | 8 |
587 * | | | |
588 * | | 7 +-----+
589 * | | | |
590 * | |-----+ |
591 * | 6 | | |
592 * +-----+ | |
593 * | | | |
594 * | 1 | 2 | 3 |
595 * +-----+-----+-----+
596 *
597 */
598static void
599splat_taskq_test6_func(void *arg)
600{
601 splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg;
602 splat_taskq_arg_t *tq_arg = tq_id->arg;
603 int factor;
604
605 /* Delays determined by above table */
606 switch (tq_id->id) {
607 default: factor = 0; break;
608 case 1: factor = 2; break;
609 case 2: case 4: case 5: factor = 4; break;
610 case 6: case 7: case 8: factor = 5; break;
611 case 3: factor = 6; break;
612 }
613
614 msleep(factor * 100);
615
616 splat_vprint(tq_arg->file, tq_arg->name,
617 "Taskqid %d complete for taskq '%s'\n",
618 tq_id->id, tq_arg->name);
619
620 spin_lock(&tq_arg->lock);
621 tq_arg->order[tq_arg->flag] = tq_id->id;
622 tq_arg->flag++;
623 spin_unlock(&tq_arg->lock);
624}
625
626static int
627splat_taskq_test6(struct file *file, void *arg)
628{
629 taskq_t *tq;
630 taskqid_t id;
631 splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX];
632 splat_taskq_arg_t tq_arg;
633 int order[SPLAT_TASKQ_ORDER_MAX] = { 1,2,3,6,7,8,4,5 };
634 int i, rc = 0;
635 uint_t tflags;
636
637 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' creating\n",
638 SPLAT_TASKQ_TEST6_NAME);
639 if ((tq = taskq_create(SPLAT_TASKQ_TEST6_NAME, 3, maxclsyspri,
640 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
641 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
642 "Taskq '%s' create failed\n",
643 SPLAT_TASKQ_TEST6_NAME);
644 return -EINVAL;
645 }
646
647 tq_arg.flag = 0;
648 memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX);
649 spin_lock_init(&tq_arg.lock);
650 tq_arg.file = file;
651 tq_arg.name = SPLAT_TASKQ_TEST6_NAME;
652
653 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
654 tq_id[i].id = i + 1;
655 tq_id[i].arg = &tq_arg;
656 tflags = TQ_SLEEP;
657 if (i > 4)
658 tflags |= TQ_FRONT;
659
660 if ((id = taskq_dispatch(tq, splat_taskq_test6_func,
661 &tq_id[i], tflags)) == 0) {
662 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
663 "Taskq '%s' function '%s' dispatch failed\n",
664 tq_arg.name, sym2str(splat_taskq_test6_func));
665 rc = -EINVAL;
666 goto out;
667 }
668
669 if (tq_id[i].id != id) {
670 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
671 "Taskq '%s' expected taskqid %d got %d\n",
672 tq_arg.name, (int)tq_id[i].id, (int)id);
673 rc = -EINVAL;
674 goto out;
675 }
676 /* Sleep to let tasks 1-3 start executing. */
677 if ( i == 2 )
678 msleep(100);
679 }
680
681 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' "
682 "waiting for taskqid %d completion\n", tq_arg.name,
683 SPLAT_TASKQ_ORDER_MAX);
684 taskq_wait_id(tq, SPLAT_TASKQ_ORDER_MAX);
685 rc = splat_taskq_test_order(&tq_arg, order);
686
687out:
688 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
689 "Taskq '%s' destroying\n", tq_arg.name);
690 taskq_destroy(tq);
691
692 return rc;
693}
694
ac1e5b60
PS
695static void
696splat_taskq_test7_func(void *arg)
697{
698 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
699 taskqid_t id;
700
701 ASSERT(tq_arg);
702
703 if (tq_arg->depth >= SPLAT_TASKQ_DEPTH_MAX)
704 return;
705
706 tq_arg->depth++;
707
708 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST7_NAME,
709 "Taskq '%s' function '%s' dispatching (depth = %u)\n",
710 tq_arg->name, sym2str(splat_taskq_test7_func),
711 tq_arg->depth);
712
713 if ((id = taskq_dispatch(tq_arg->tq, splat_taskq_test7_func,
714 tq_arg, TQ_SLEEP)) == 0) {
715 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST7_NAME,
716 "Taskq '%s' function '%s' dispatch failed "
717 "(depth = %u)\n", tq_arg->name,
718 sym2str(splat_taskq_test7_func), tq_arg->depth);
719 tq_arg->flag = -EINVAL;
720 return;
721 }
722}
723
724static int
725splat_taskq_test7(struct file *file, void *arg)
726{
727 taskq_t *tq;
728 splat_taskq_arg_t tq_arg;
729
730 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
731 "Taskq '%s' creating\n", SPLAT_TASKQ_TEST7_NAME);
732 if ((tq = taskq_create(SPLAT_TASKQ_TEST7_NAME, 1, maxclsyspri,
733 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
734 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
735 "Taskq '%s' create failed\n",
736 SPLAT_TASKQ_TEST7_NAME);
737 return -EINVAL;
738 }
739
740 tq_arg.depth = 0;
741 tq_arg.flag = 0;
742 tq_arg.id = 0;
743 tq_arg.file = file;
744 tq_arg.name = SPLAT_TASKQ_TEST7_NAME;
745 tq_arg.tq = tq;
746
747 splat_taskq_test7_func(&tq_arg);
748
749 if (tq_arg.flag == 0) {
750 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
751 "Taskq '%s' waiting\n", tq_arg.name);
752 taskq_wait_id(tq, SPLAT_TASKQ_DEPTH_MAX);
753 }
754
755 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
756 "Taskq '%s' destroying\n", tq_arg.name);
757 taskq_destroy(tq);
758
759 return tq_arg.depth == SPLAT_TASKQ_DEPTH_MAX ? 0 : -EINVAL;
760}
761
7c50328b 762splat_subsystem_t *
763splat_taskq_init(void)
f1ca4da6 764{
7c50328b 765 splat_subsystem_t *sub;
f1ca4da6 766
767 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
768 if (sub == NULL)
769 return NULL;
770
771 memset(sub, 0, sizeof(*sub));
7c50328b 772 strncpy(sub->desc.name, SPLAT_TASKQ_NAME, SPLAT_NAME_SIZE);
773 strncpy(sub->desc.desc, SPLAT_TASKQ_DESC, SPLAT_DESC_SIZE);
f1ca4da6 774 INIT_LIST_HEAD(&sub->subsystem_list);
775 INIT_LIST_HEAD(&sub->test_list);
776 spin_lock_init(&sub->test_lock);
7c50328b 777 sub->desc.id = SPLAT_SUBSYSTEM_TASKQ;
f1ca4da6 778
7c50328b 779 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST1_NAME, SPLAT_TASKQ_TEST1_DESC,
780 SPLAT_TASKQ_TEST1_ID, splat_taskq_test1);
781 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST2_NAME, SPLAT_TASKQ_TEST2_DESC,
782 SPLAT_TASKQ_TEST2_ID, splat_taskq_test2);
e9cb2b4f
BB
783 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST3_NAME, SPLAT_TASKQ_TEST3_DESC,
784 SPLAT_TASKQ_TEST3_ID, splat_taskq_test3);
7257ec41
BB
785 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST4_NAME, SPLAT_TASKQ_TEST4_DESC,
786 SPLAT_TASKQ_TEST4_ID, splat_taskq_test4);
5562e5d1
BB
787 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST5_NAME, SPLAT_TASKQ_TEST5_DESC,
788 SPLAT_TASKQ_TEST5_ID, splat_taskq_test5);
55f10ae5
NB
789 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST6_NAME, SPLAT_TASKQ_TEST6_DESC,
790 SPLAT_TASKQ_TEST6_ID, splat_taskq_test6);
ac1e5b60
PS
791 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST7_NAME, SPLAT_TASKQ_TEST7_DESC,
792 SPLAT_TASKQ_TEST7_ID, splat_taskq_test7);
f1ca4da6 793
794 return sub;
795}
796
797void
7c50328b 798splat_taskq_fini(splat_subsystem_t *sub)
f1ca4da6 799{
800 ASSERT(sub);
ac1e5b60 801 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST7_ID);
ed948fa7 802 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST6_ID);
5562e5d1 803 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST5_ID);
7257ec41 804 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST4_ID);
e9cb2b4f 805 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST3_ID);
7c50328b 806 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST2_ID);
807 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST1_ID);
f1ca4da6 808
809 kfree(sub);
810}
811
812int
7c50328b 813splat_taskq_id(void) {
814 return SPLAT_SUBSYSTEM_TASKQ;
f1ca4da6 815}