]> git.proxmox.com Git - mirror_spl-debian.git/blob - module/splat/splat-taskq.c
Implementation of a regression test for TQ_FRONT.
[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_ORDER_MAX 8
57
58 typedef struct splat_taskq_arg {
59 int flag;
60 int id;
61 atomic_t count;
62 int order[SPLAT_TASKQ_ORDER_MAX];
63 spinlock_t lock;
64 struct file *file;
65 const char *name;
66 } splat_taskq_arg_t;
67
68 typedef struct splat_taskq_id {
69 int id;
70 splat_taskq_arg_t *arg;
71 } splat_taskq_id_t;
72
73 /*
74 * Create a taskq, queue a task, wait until task completes, ensure
75 * task ran properly, cleanup taskq.
76 */
77 static void
78 splat_taskq_test13_func(void *arg)
79 {
80 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
81
82 ASSERT(tq_arg);
83 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST1_NAME,
84 "Taskq '%s' function '%s' setting flag\n",
85 tq_arg->name, sym2str(splat_taskq_test13_func));
86 tq_arg->flag = 1;
87 }
88
89 static int
90 splat_taskq_test1(struct file *file, void *arg)
91 {
92 taskq_t *tq;
93 taskqid_t id;
94 splat_taskq_arg_t tq_arg;
95
96 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' creating\n",
97 SPLAT_TASKQ_TEST1_NAME);
98 if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, maxclsyspri,
99 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
100 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
101 "Taskq '%s' create failed\n",
102 SPLAT_TASKQ_TEST1_NAME);
103 return -EINVAL;
104 }
105
106 tq_arg.flag = 0;
107 tq_arg.id = 0;
108 tq_arg.file = file;
109 tq_arg.name = SPLAT_TASKQ_TEST1_NAME;
110
111 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
112 "Taskq '%s' function '%s' dispatching\n",
113 tq_arg.name, sym2str(splat_taskq_test13_func));
114 if ((id = taskq_dispatch(tq, splat_taskq_test13_func,
115 &tq_arg, TQ_SLEEP)) == 0) {
116 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
117 "Taskq '%s' function '%s' dispatch failed\n",
118 tq_arg.name, sym2str(splat_taskq_test13_func));
119 taskq_destroy(tq);
120 return -EINVAL;
121 }
122
123 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
124 tq_arg.name);
125 taskq_wait(tq);
126 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
127 tq_arg.name);
128 taskq_destroy(tq);
129
130 return (tq_arg.flag) ? 0 : -EINVAL;
131 }
132
133 /*
134 * Create multiple taskq's, each with multiple tasks, wait until
135 * all tasks complete, ensure all tasks ran properly and in the
136 * correct order. Run order must be the same as the order submitted
137 * because we only have 1 thread per taskq. Finally cleanup the taskq.
138 */
139 static void
140 splat_taskq_test2_func1(void *arg)
141 {
142 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
143
144 ASSERT(tq_arg);
145 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
146 "Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
147 tq_arg->name, tq_arg->id,
148 sym2str(splat_taskq_test2_func1),
149 tq_arg->flag * 2, tq_arg->flag);
150 tq_arg->flag *= 2;
151 }
152
153 static void
154 splat_taskq_test2_func2(void *arg)
155 {
156 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
157
158 ASSERT(tq_arg);
159 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
160 "Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
161 tq_arg->name, tq_arg->id,
162 sym2str(splat_taskq_test2_func2),
163 tq_arg->flag + 1, tq_arg->flag);
164 tq_arg->flag += 1;
165 }
166
167 #define TEST2_TASKQS 8
168 #define TEST2_THREADS_PER_TASKQ 1
169
170 static int
171 splat_taskq_test2(struct file *file, void *arg) {
172 taskq_t *tq[TEST2_TASKQS] = { NULL };
173 taskqid_t id;
174 splat_taskq_arg_t tq_args[TEST2_TASKQS];
175 int i, rc = 0;
176
177 for (i = 0; i < TEST2_TASKQS; i++) {
178
179 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq '%s/%d' "
180 "creating\n", SPLAT_TASKQ_TEST2_NAME, i);
181 if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME,
182 TEST2_THREADS_PER_TASKQ,
183 maxclsyspri, 50, INT_MAX,
184 TASKQ_PREPOPULATE)) == NULL) {
185 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
186 "Taskq '%s/%d' create failed\n",
187 SPLAT_TASKQ_TEST2_NAME, i);
188 rc = -EINVAL;
189 break;
190 }
191
192 tq_args[i].flag = i;
193 tq_args[i].id = i;
194 tq_args[i].file = file;
195 tq_args[i].name = SPLAT_TASKQ_TEST2_NAME;
196
197 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
198 "Taskq '%s/%d' function '%s' dispatching\n",
199 tq_args[i].name, tq_args[i].id,
200 sym2str(splat_taskq_test2_func1));
201 if ((id = taskq_dispatch(
202 tq[i], splat_taskq_test2_func1,
203 &tq_args[i], TQ_SLEEP)) == 0) {
204 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
205 "Taskq '%s/%d' function '%s' dispatch "
206 "failed\n", tq_args[i].name, tq_args[i].id,
207 sym2str(splat_taskq_test2_func1));
208 rc = -EINVAL;
209 break;
210 }
211
212 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
213 "Taskq '%s/%d' function '%s' dispatching\n",
214 tq_args[i].name, tq_args[i].id,
215 sym2str(splat_taskq_test2_func2));
216 if ((id = taskq_dispatch(
217 tq[i], splat_taskq_test2_func2,
218 &tq_args[i], TQ_SLEEP)) == 0) {
219 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
220 "Taskq '%s/%d' function '%s' dispatch failed\n",
221 tq_args[i].name, tq_args[i].id,
222 sym2str(splat_taskq_test2_func2));
223 rc = -EINVAL;
224 break;
225 }
226 }
227
228 /* When rc is set we're effectively just doing cleanup here, so
229 * ignore new errors in that case. They just cause noise. */
230 for (i = 0; i < TEST2_TASKQS; i++) {
231 if (tq[i] != NULL) {
232 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
233 "Taskq '%s/%d' waiting\n",
234 tq_args[i].name, tq_args[i].id);
235 taskq_wait(tq[i]);
236 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
237 "Taskq '%s/%d; destroying\n",
238 tq_args[i].name, tq_args[i].id);
239 taskq_destroy(tq[i]);
240
241 if (!rc && tq_args[i].flag != ((i * 2) + 1)) {
242 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
243 "Taskq '%s/%d' processed tasks "
244 "out of order; %d != %d\n",
245 tq_args[i].name, tq_args[i].id,
246 tq_args[i].flag, i * 2 + 1);
247 rc = -EINVAL;
248 } else {
249 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
250 "Taskq '%s/%d' processed tasks "
251 "in the correct order; %d == %d\n",
252 tq_args[i].name, tq_args[i].id,
253 tq_args[i].flag, i * 2 + 1);
254 }
255 }
256 }
257
258 return rc;
259 }
260
261 /*
262 * Use the global system task queue with a single task, wait until task
263 * completes, ensure task ran properly.
264 */
265 static int
266 splat_taskq_test3(struct file *file, void *arg)
267 {
268 taskqid_t id;
269 splat_taskq_arg_t tq_arg;
270
271 tq_arg.flag = 0;
272 tq_arg.id = 0;
273 tq_arg.file = file;
274 tq_arg.name = SPLAT_TASKQ_TEST3_NAME;
275
276 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
277 "Taskq '%s' function '%s' dispatching\n",
278 tq_arg.name, sym2str(splat_taskq_test13_func));
279 if ((id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
280 &tq_arg, TQ_SLEEP)) == 0) {
281 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
282 "Taskq '%s' function '%s' dispatch failed\n",
283 tq_arg.name, sym2str(splat_taskq_test13_func));
284 return -EINVAL;
285 }
286
287 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
288 tq_arg.name);
289 taskq_wait(system_taskq);
290
291 return (tq_arg.flag) ? 0 : -EINVAL;
292 }
293
294 /*
295 * Create a taskq and dispatch a large number of tasks to the queue.
296 * Then use taskq_wait() to block until all the tasks complete, then
297 * cross check that all the tasks ran by checking tg_arg->count which
298 * is incremented in the task function. Finally cleanup the taskq.
299 */
300 static void
301 splat_taskq_test4_func(void *arg)
302 {
303 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
304 ASSERT(tq_arg);
305
306 atomic_inc(&tq_arg->count);
307 }
308
309 static int
310 splat_taskq_test4(struct file *file, void *arg)
311 {
312 taskq_t *tq;
313 splat_taskq_arg_t tq_arg;
314 int i, j, rc = 0;
315
316 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' creating\n",
317 SPLAT_TASKQ_TEST4_NAME);
318 if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, maxclsyspri,
319 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
320 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
321 "Taskq '%s' create failed\n",
322 SPLAT_TASKQ_TEST4_NAME);
323 return -EINVAL;
324 }
325
326 tq_arg.file = file;
327 tq_arg.name = SPLAT_TASKQ_TEST4_NAME;
328
329 for (i = 1; i <= 1024; i *= 2) {
330 atomic_set(&tq_arg.count, 0);
331 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
332 "Taskq '%s' function '%s' dispatched %d times\n",
333 tq_arg.name, sym2str(splat_taskq_test4_func), i);
334
335 for (j = 0; j < i; j++) {
336 if ((taskq_dispatch(tq, splat_taskq_test4_func,
337 &tq_arg, TQ_SLEEP)) == 0) {
338 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
339 "Taskq '%s' function '%s' dispatch "
340 "%d failed\n", tq_arg.name,
341 sym2str(splat_taskq_test13_func), j);
342 rc = -EINVAL;
343 goto out;
344 }
345 }
346
347 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
348 "waiting for %d dispatches\n", tq_arg.name, i);
349 taskq_wait(tq);
350 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
351 "%d/%d dispatches finished\n", tq_arg.name,
352 atomic_read(&tq_arg.count), i);
353 if (atomic_read(&tq_arg.count) != i) {
354 rc = -ERANGE;
355 goto out;
356
357 }
358 }
359 out:
360 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' destroying\n",
361 tq_arg.name);
362 taskq_destroy(tq);
363
364 return rc;
365 }
366
367 /*
368 * Create a taskq and dispatch a specific sequence of tasks carefully
369 * crafted to validate the order in which tasks are processed. When
370 * there are multiple worker threads each thread will process the
371 * next pending task as soon as it completes its current task. This
372 * means that tasks do not strictly complete in order in which they
373 * were dispatched (increasing task id). This is fine but we need to
374 * verify that taskq_wait_id() blocks until the passed task id and all
375 * lower task ids complete. We do this by dispatching the following
376 * specific sequence of tasks each of which block for N time units.
377 * We then use taskq_wait_id() to unblock at specific task id and
378 * verify the only the expected task ids have completed and in the
379 * correct order. The two cases of interest are:
380 *
381 * 1) Task ids larger than the waited for task id can run and
382 * complete as long as there is an available worker thread.
383 * 2) All task ids lower than the waited one must complete before
384 * unblocking even if the waited task id itself has completed.
385 *
386 * The following table shows each task id and how they will be
387 * scheduled. Each rows represent one time unit and each column
388 * one of the three worker threads. The places taskq_wait_id()
389 * must unblock for a specific id are identified as well as the
390 * task ids which must have completed and their order.
391 *
392 * +-----+ <--- taskq_wait_id(tq, 8) unblocks
393 * | | Required Completion Order: 1,2,4,5,3,8,6,7
394 * +-----+ |
395 * | | |
396 * | | +-----+
397 * | | | 8 |
398 * | | +-----+ <--- taskq_wait_id(tq, 3) unblocks
399 * | | 7 | | Required Completion Order: 1,2,4,5,3
400 * | +-----+ |
401 * | 6 | | |
402 * +-----+ | |
403 * | | 5 | |
404 * | +-----+ |
405 * | 4 | | |
406 * +-----+ | |
407 * | 1 | 2 | 3 |
408 * +-----+-----+-----+
409 *
410 */
411 static void
412 splat_taskq_test5_func(void *arg)
413 {
414 splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg;
415 splat_taskq_arg_t *tq_arg = tq_id->arg;
416 int factor;
417
418 /* Delays determined by above table */
419 switch (tq_id->id) {
420 default: factor = 0; break;
421 case 1: case 8: factor = 1; break;
422 case 2: case 4: case 5: factor = 2; break;
423 case 6: case 7: factor = 4; break;
424 case 3: factor = 5; break;
425 }
426
427 msleep(factor * 100);
428 splat_vprint(tq_arg->file, tq_arg->name,
429 "Taskqid %d complete for taskq '%s'\n",
430 tq_id->id, tq_arg->name);
431
432 spin_lock(&tq_arg->lock);
433 tq_arg->order[tq_arg->flag] = tq_id->id;
434 tq_arg->flag++;
435 spin_unlock(&tq_arg->lock);
436 }
437
438 static int
439 splat_taskq_test_order(splat_taskq_arg_t *tq_arg, int *order)
440 {
441 int i, j;
442
443 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
444 if (tq_arg->order[i] != order[i]) {
445 splat_vprint(tq_arg->file, tq_arg->name,
446 "Taskq '%s' incorrect completion "
447 "order\n", tq_arg->name);
448 splat_vprint(tq_arg->file, tq_arg->name,
449 "%s", "Expected { ");
450
451 for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++)
452 splat_print(tq_arg->file, "%d ", order[j]);
453
454 splat_print(tq_arg->file, "%s", "}\n");
455 splat_vprint(tq_arg->file, tq_arg->name,
456 "%s", "Got { ");
457
458 for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++)
459 splat_print(tq_arg->file, "%d ",
460 tq_arg->order[j]);
461
462 splat_print(tq_arg->file, "%s", "}\n");
463 return -EILSEQ;
464 }
465 }
466
467 splat_vprint(tq_arg->file, tq_arg->name,
468 "Taskq '%s' validated correct completion order\n",
469 tq_arg->name);
470
471 return 0;
472 }
473
474 static int
475 splat_taskq_test5(struct file *file, void *arg)
476 {
477 taskq_t *tq;
478 taskqid_t id;
479 splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX];
480 splat_taskq_arg_t tq_arg;
481 int order1[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,0,0,0 };
482 int order2[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,8,6,7 };
483 int i, rc = 0;
484
485 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' creating\n",
486 SPLAT_TASKQ_TEST5_NAME);
487 if ((tq = taskq_create(SPLAT_TASKQ_TEST5_NAME, 3, maxclsyspri,
488 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
489 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
490 "Taskq '%s' create failed\n",
491 SPLAT_TASKQ_TEST5_NAME);
492 return -EINVAL;
493 }
494
495 tq_arg.flag = 0;
496 memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX);
497 spin_lock_init(&tq_arg.lock);
498 tq_arg.file = file;
499 tq_arg.name = SPLAT_TASKQ_TEST5_NAME;
500
501 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
502 tq_id[i].id = i + 1;
503 tq_id[i].arg = &tq_arg;
504
505 if ((id = taskq_dispatch(tq, splat_taskq_test5_func,
506 &tq_id[i], TQ_SLEEP)) == 0) {
507 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
508 "Taskq '%s' function '%s' dispatch failed\n",
509 tq_arg.name, sym2str(splat_taskq_test5_func));
510 rc = -EINVAL;
511 goto out;
512 }
513
514 if (tq_id[i].id != id) {
515 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
516 "Taskq '%s' expected taskqid %d got %d\n",
517 tq_arg.name, (int)tq_id[i].id, (int)id);
518 rc = -EINVAL;
519 goto out;
520 }
521 }
522
523 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' "
524 "waiting for taskqid %d completion\n", tq_arg.name, 3);
525 taskq_wait_id(tq, 3);
526 if ((rc = splat_taskq_test_order(&tq_arg, order1)))
527 goto out;
528
529 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' "
530 "waiting for taskqid %d completion\n", tq_arg.name, 8);
531 taskq_wait_id(tq, 8);
532 rc = splat_taskq_test_order(&tq_arg, order2);
533
534 out:
535 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
536 "Taskq '%s' destroying\n", tq_arg.name);
537 taskq_destroy(tq);
538
539 return rc;
540 }
541
542 /*
543 * Create a single task queue with three threads. Dispatch 8 tasks,
544 * setting TQ_FRONT on only the last three. Sleep after
545 * dispatching tasks 1-3 to ensure they will run and hold the threads
546 * busy while we dispatch the remaining tasks. Verify that tasks 6-8
547 * run before task 4-5.
548 *
549 * The following table shows each task id and how they will be
550 * scheduled. Each rows represent one time unit and each column
551 * one of the three worker threads.
552 *
553 * +-----+
554 * | |
555 * +-----+ |
556 * | | 5 +-----+
557 * | | | |
558 * | +-----| |
559 * | 4 | | |
560 * +-----+ | 8 |
561 * | | | |
562 * | | 7 +-----+
563 * | | | |
564 * | |-----+ |
565 * | 6 | | |
566 * +-----+ | |
567 * | | | |
568 * | 1 | 2 | 3 |
569 * +-----+-----+-----+
570 *
571 */
572 static void
573 splat_taskq_test6_func(void *arg)
574 {
575 splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg;
576 splat_taskq_arg_t *tq_arg = tq_id->arg;
577 int factor;
578
579 /* Delays determined by above table */
580 switch (tq_id->id) {
581 default: factor = 0; break;
582 case 1: factor = 2; break;
583 case 2: case 4: case 5: factor = 4; break;
584 case 6: case 7: case 8: factor = 5; break;
585 case 3: factor = 6; break;
586 }
587
588 msleep(factor * 100);
589
590 splat_vprint(tq_arg->file, tq_arg->name,
591 "Taskqid %d complete for taskq '%s'\n",
592 tq_id->id, tq_arg->name);
593
594 spin_lock(&tq_arg->lock);
595 tq_arg->order[tq_arg->flag] = tq_id->id;
596 tq_arg->flag++;
597 spin_unlock(&tq_arg->lock);
598 }
599
600 static int
601 splat_taskq_test6(struct file *file, void *arg)
602 {
603 taskq_t *tq;
604 taskqid_t id;
605 splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX];
606 splat_taskq_arg_t tq_arg;
607 int order[SPLAT_TASKQ_ORDER_MAX] = { 1,2,3,6,7,8,4,5 };
608 int i, rc = 0;
609 uint_t tflags;
610
611 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' creating\n",
612 SPLAT_TASKQ_TEST6_NAME);
613 if ((tq = taskq_create(SPLAT_TASKQ_TEST6_NAME, 3, maxclsyspri,
614 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
615 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
616 "Taskq '%s' create failed\n",
617 SPLAT_TASKQ_TEST6_NAME);
618 return -EINVAL;
619 }
620
621 tq_arg.flag = 0;
622 memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX);
623 spin_lock_init(&tq_arg.lock);
624 tq_arg.file = file;
625 tq_arg.name = SPLAT_TASKQ_TEST6_NAME;
626
627 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
628 tq_id[i].id = i + 1;
629 tq_id[i].arg = &tq_arg;
630 tflags = TQ_SLEEP;
631 if (i > 4)
632 tflags |= TQ_FRONT;
633
634 if ((id = taskq_dispatch(tq, splat_taskq_test6_func,
635 &tq_id[i], tflags)) == 0) {
636 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
637 "Taskq '%s' function '%s' dispatch failed\n",
638 tq_arg.name, sym2str(splat_taskq_test6_func));
639 rc = -EINVAL;
640 goto out;
641 }
642
643 if (tq_id[i].id != id) {
644 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
645 "Taskq '%s' expected taskqid %d got %d\n",
646 tq_arg.name, (int)tq_id[i].id, (int)id);
647 rc = -EINVAL;
648 goto out;
649 }
650 /* Sleep to let tasks 1-3 start executing. */
651 if ( i == 2 )
652 msleep(100);
653 }
654
655 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' "
656 "waiting for taskqid %d completion\n", tq_arg.name,
657 SPLAT_TASKQ_ORDER_MAX);
658 taskq_wait_id(tq, SPLAT_TASKQ_ORDER_MAX);
659 rc = splat_taskq_test_order(&tq_arg, order);
660
661 out:
662 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
663 "Taskq '%s' destroying\n", tq_arg.name);
664 taskq_destroy(tq);
665
666 return rc;
667 }
668
669 splat_subsystem_t *
670 splat_taskq_init(void)
671 {
672 splat_subsystem_t *sub;
673
674 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
675 if (sub == NULL)
676 return NULL;
677
678 memset(sub, 0, sizeof(*sub));
679 strncpy(sub->desc.name, SPLAT_TASKQ_NAME, SPLAT_NAME_SIZE);
680 strncpy(sub->desc.desc, SPLAT_TASKQ_DESC, SPLAT_DESC_SIZE);
681 INIT_LIST_HEAD(&sub->subsystem_list);
682 INIT_LIST_HEAD(&sub->test_list);
683 spin_lock_init(&sub->test_lock);
684 sub->desc.id = SPLAT_SUBSYSTEM_TASKQ;
685
686 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST1_NAME, SPLAT_TASKQ_TEST1_DESC,
687 SPLAT_TASKQ_TEST1_ID, splat_taskq_test1);
688 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST2_NAME, SPLAT_TASKQ_TEST2_DESC,
689 SPLAT_TASKQ_TEST2_ID, splat_taskq_test2);
690 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST3_NAME, SPLAT_TASKQ_TEST3_DESC,
691 SPLAT_TASKQ_TEST3_ID, splat_taskq_test3);
692 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST4_NAME, SPLAT_TASKQ_TEST4_DESC,
693 SPLAT_TASKQ_TEST4_ID, splat_taskq_test4);
694 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST5_NAME, SPLAT_TASKQ_TEST5_DESC,
695 SPLAT_TASKQ_TEST5_ID, splat_taskq_test5);
696 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST6_NAME, SPLAT_TASKQ_TEST6_DESC,
697 SPLAT_TASKQ_TEST6_ID, splat_taskq_test6);
698
699 return sub;
700 }
701
702 void
703 splat_taskq_fini(splat_subsystem_t *sub)
704 {
705 ASSERT(sub);
706 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST5_ID);
707 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST4_ID);
708 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST3_ID);
709 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST2_ID);
710 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST1_ID);
711
712 kfree(sub);
713 }
714
715 int
716 splat_taskq_id(void) {
717 return SPLAT_SUBSYSTEM_TASKQ;
718 }