]> git.proxmox.com Git - mirror_spl-debian.git/blob - module/splat/splat-taskq.c
Add Test: "Single task queue, recursive dispatch"
[mirror_spl-debian.git] / module / splat / splat-taskq.c
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>.
6 * UCRL-CODE-235197
7 *
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.
15 *
16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
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
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting LAyer Tests (SPLAT) Task Queue Tests.
25 \*****************************************************************************/
26
27 #include "splat-internal.h"
28
29 #define SPLAT_TASKQ_NAME "taskq"
30 #define SPLAT_TASKQ_DESC "Kernel Task Queue Tests"
31
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"
35
36 #define SPLAT_TASKQ_TEST2_ID 0x0202
37 #define SPLAT_TASKQ_TEST2_NAME "multiple"
38 #define SPLAT_TASKQ_TEST2_DESC "Multiple task queues, multiple tasks"
39
40 #define SPLAT_TASKQ_TEST3_ID 0x0203
41 #define SPLAT_TASKQ_TEST3_NAME "system"
42 #define SPLAT_TASKQ_TEST3_DESC "System task queue, multiple tasks"
43
44 #define SPLAT_TASKQ_TEST4_ID 0x0204
45 #define SPLAT_TASKQ_TEST4_NAME "wait"
46 #define SPLAT_TASKQ_TEST4_DESC "Multiple task waiting"
47
48 #define SPLAT_TASKQ_TEST5_ID 0x0205
49 #define SPLAT_TASKQ_TEST5_NAME "order"
50 #define SPLAT_TASKQ_TEST5_DESC "Correct task ordering"
51
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
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
60 #define SPLAT_TASKQ_ORDER_MAX 8
61 #define SPLAT_TASKQ_DEPTH_MAX 16
62
63 typedef struct splat_taskq_arg {
64 int flag;
65 int id;
66 atomic_t count;
67 int order[SPLAT_TASKQ_ORDER_MAX];
68 unsigned int depth;
69 taskq_t *tq;
70 spinlock_t lock;
71 struct file *file;
72 const char *name;
73 } splat_taskq_arg_t;
74
75 typedef 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.
83 */
84 static void
85 splat_taskq_test13_func(void *arg)
86 {
87 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
88
89 ASSERT(tq_arg);
90 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST1_NAME,
91 "Taskq '%s' function '%s' setting flag\n",
92 tq_arg->name, sym2str(splat_taskq_test13_func));
93 tq_arg->flag = 1;
94 }
95
96 static int
97 splat_taskq_test1(struct file *file, void *arg)
98 {
99 taskq_t *tq;
100 taskqid_t id;
101 splat_taskq_arg_t tq_arg;
102
103 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' creating\n",
104 SPLAT_TASKQ_TEST1_NAME);
105 if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, maxclsyspri,
106 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
107 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
108 "Taskq '%s' create failed\n",
109 SPLAT_TASKQ_TEST1_NAME);
110 return -EINVAL;
111 }
112
113 tq_arg.flag = 0;
114 tq_arg.id = 0;
115 tq_arg.file = file;
116 tq_arg.name = SPLAT_TASKQ_TEST1_NAME;
117
118 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
119 "Taskq '%s' function '%s' dispatching\n",
120 tq_arg.name, sym2str(splat_taskq_test13_func));
121 if ((id = taskq_dispatch(tq, splat_taskq_test13_func,
122 &tq_arg, TQ_SLEEP)) == 0) {
123 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
124 "Taskq '%s' function '%s' dispatch failed\n",
125 tq_arg.name, sym2str(splat_taskq_test13_func));
126 taskq_destroy(tq);
127 return -EINVAL;
128 }
129
130 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
131 tq_arg.name);
132 taskq_wait(tq);
133 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
134 tq_arg.name);
135 taskq_destroy(tq);
136
137 return (tq_arg.flag) ? 0 : -EINVAL;
138 }
139
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.
145 */
146 static void
147 splat_taskq_test2_func1(void *arg)
148 {
149 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
150
151 ASSERT(tq_arg);
152 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
153 "Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
154 tq_arg->name, tq_arg->id,
155 sym2str(splat_taskq_test2_func1),
156 tq_arg->flag * 2, tq_arg->flag);
157 tq_arg->flag *= 2;
158 }
159
160 static void
161 splat_taskq_test2_func2(void *arg)
162 {
163 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
164
165 ASSERT(tq_arg);
166 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
167 "Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
168 tq_arg->name, tq_arg->id,
169 sym2str(splat_taskq_test2_func2),
170 tq_arg->flag + 1, tq_arg->flag);
171 tq_arg->flag += 1;
172 }
173
174 #define TEST2_TASKQS 8
175 #define TEST2_THREADS_PER_TASKQ 1
176
177 static int
178 splat_taskq_test2(struct file *file, void *arg) {
179 taskq_t *tq[TEST2_TASKQS] = { NULL };
180 taskqid_t id;
181 splat_taskq_arg_t tq_args[TEST2_TASKQS];
182 int i, rc = 0;
183
184 for (i = 0; i < TEST2_TASKQS; i++) {
185
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,
189 TEST2_THREADS_PER_TASKQ,
190 maxclsyspri, 50, INT_MAX,
191 TASKQ_PREPOPULATE)) == NULL) {
192 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
193 "Taskq '%s/%d' create failed\n",
194 SPLAT_TASKQ_TEST2_NAME, i);
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;
202 tq_args[i].name = SPLAT_TASKQ_TEST2_NAME;
203
204 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
205 "Taskq '%s/%d' function '%s' dispatching\n",
206 tq_args[i].name, tq_args[i].id,
207 sym2str(splat_taskq_test2_func1));
208 if ((id = taskq_dispatch(
209 tq[i], splat_taskq_test2_func1,
210 &tq_args[i], TQ_SLEEP)) == 0) {
211 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
212 "Taskq '%s/%d' function '%s' dispatch "
213 "failed\n", tq_args[i].name, tq_args[i].id,
214 sym2str(splat_taskq_test2_func1));
215 rc = -EINVAL;
216 break;
217 }
218
219 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
220 "Taskq '%s/%d' function '%s' dispatching\n",
221 tq_args[i].name, tq_args[i].id,
222 sym2str(splat_taskq_test2_func2));
223 if ((id = taskq_dispatch(
224 tq[i], splat_taskq_test2_func2,
225 &tq_args[i], TQ_SLEEP)) == 0) {
226 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
227 "Taskq '%s/%d' function '%s' dispatch failed\n",
228 tq_args[i].name, tq_args[i].id,
229 sym2str(splat_taskq_test2_func2));
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) {
239 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
240 "Taskq '%s/%d' waiting\n",
241 tq_args[i].name, tq_args[i].id);
242 taskq_wait(tq[i]);
243 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
244 "Taskq '%s/%d; destroying\n",
245 tq_args[i].name, tq_args[i].id);
246 taskq_destroy(tq[i]);
247
248 if (!rc && tq_args[i].flag != ((i * 2) + 1)) {
249 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
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 {
256 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
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
268 /*
269 * Use the global system task queue with a single task, wait until task
270 * completes, ensure task ran properly.
271 */
272 static int
273 splat_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
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.
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.
310 */
311 static void
312 splat_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
320 static int
321 splat_taskq_test4_common(struct file *file, void *arg, int minalloc,
322 int maxalloc, int nr_tasks)
323 {
324 taskq_t *tq;
325 splat_taskq_arg_t tq_arg;
326 int i, j, rc = 0;
327
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);
331 if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, maxclsyspri,
332 minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
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
342 for (i = 1; i <= nr_tasks; i *= 2) {
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,
354 sym2str(splat_taskq_test4_func), j);
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 }
372 out:
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
380 static 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
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
419 * | | Required Completion Order: 1,2,4,5,3,8,6,7
420 * +-----+ |
421 * | | |
422 * | | +-----+
423 * | | | 8 |
424 * | | +-----+ <--- taskq_wait_id(tq, 3) unblocks
425 * | | 7 | | Required Completion Order: 1,2,4,5,3
426 * | +-----+ |
427 * | 6 | | |
428 * +-----+ | |
429 * | | 5 | |
430 * | +-----+ |
431 * | 4 | | |
432 * +-----+ | |
433 * | 1 | 2 | 3 |
434 * +-----+-----+-----+
435 *
436 */
437 static void
438 splat_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
464 static int
465 splat_taskq_test_order(splat_taskq_arg_t *tq_arg, int *order)
466 {
467 int i, j;
468
469 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
470 if (tq_arg->order[i] != order[i]) {
471 splat_vprint(tq_arg->file, tq_arg->name,
472 "Taskq '%s' incorrect completion "
473 "order\n", tq_arg->name);
474 splat_vprint(tq_arg->file, tq_arg->name,
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");
481 splat_vprint(tq_arg->file, tq_arg->name,
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
493 splat_vprint(tq_arg->file, tq_arg->name,
494 "Taskq '%s' validated correct completion order\n",
495 tq_arg->name);
496
497 return 0;
498 }
499
500 static int
501 splat_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);
552 if ((rc = splat_taskq_test_order(&tq_arg, order1)))
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);
558 rc = splat_taskq_test_order(&tq_arg, order2);
559
560 out:
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
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 */
598 static void
599 splat_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
626 static int
627 splat_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
687 out:
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
695 static void
696 splat_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
724 static int
725 splat_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
762 splat_subsystem_t *
763 splat_taskq_init(void)
764 {
765 splat_subsystem_t *sub;
766
767 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
768 if (sub == NULL)
769 return NULL;
770
771 memset(sub, 0, sizeof(*sub));
772 strncpy(sub->desc.name, SPLAT_TASKQ_NAME, SPLAT_NAME_SIZE);
773 strncpy(sub->desc.desc, SPLAT_TASKQ_DESC, SPLAT_DESC_SIZE);
774 INIT_LIST_HEAD(&sub->subsystem_list);
775 INIT_LIST_HEAD(&sub->test_list);
776 spin_lock_init(&sub->test_lock);
777 sub->desc.id = SPLAT_SUBSYSTEM_TASKQ;
778
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);
783 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST3_NAME, SPLAT_TASKQ_TEST3_DESC,
784 SPLAT_TASKQ_TEST3_ID, splat_taskq_test3);
785 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST4_NAME, SPLAT_TASKQ_TEST4_DESC,
786 SPLAT_TASKQ_TEST4_ID, splat_taskq_test4);
787 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST5_NAME, SPLAT_TASKQ_TEST5_DESC,
788 SPLAT_TASKQ_TEST5_ID, splat_taskq_test5);
789 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST6_NAME, SPLAT_TASKQ_TEST6_DESC,
790 SPLAT_TASKQ_TEST6_ID, splat_taskq_test6);
791 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST7_NAME, SPLAT_TASKQ_TEST7_DESC,
792 SPLAT_TASKQ_TEST7_ID, splat_taskq_test7);
793
794 return sub;
795 }
796
797 void
798 splat_taskq_fini(splat_subsystem_t *sub)
799 {
800 ASSERT(sub);
801 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST7_ID);
802 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST6_ID);
803 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST5_ID);
804 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST4_ID);
805 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST3_ID);
806 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST2_ID);
807 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST1_ID);
808
809 kfree(sub);
810 }
811
812 int
813 splat_taskq_id(void) {
814 return SPLAT_SUBSYSTEM_TASKQ;
815 }