]> git.proxmox.com Git - mirror_spl.git/blob - module/splat/splat-taskq.c
splat: Cleanup headers
[mirror_spl.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 <sys/taskq.h>
28 #include <sys/kmem.h>
29 #include "splat-internal.h"
30
31 #define SPLAT_TASKQ_NAME "taskq"
32 #define SPLAT_TASKQ_DESC "Kernel Task Queue Tests"
33
34 #define SPLAT_TASKQ_TEST1_ID 0x0201
35 #define SPLAT_TASKQ_TEST1_NAME "single"
36 #define SPLAT_TASKQ_TEST1_DESC "Single task queue, single task"
37
38 #define SPLAT_TASKQ_TEST2_ID 0x0202
39 #define SPLAT_TASKQ_TEST2_NAME "multiple"
40 #define SPLAT_TASKQ_TEST2_DESC "Multiple task queues, multiple tasks"
41
42 #define SPLAT_TASKQ_TEST3_ID 0x0203
43 #define SPLAT_TASKQ_TEST3_NAME "system"
44 #define SPLAT_TASKQ_TEST3_DESC "System task queue, multiple tasks"
45
46 #define SPLAT_TASKQ_TEST4_ID 0x0204
47 #define SPLAT_TASKQ_TEST4_NAME "wait"
48 #define SPLAT_TASKQ_TEST4_DESC "Multiple task waiting"
49
50 #define SPLAT_TASKQ_TEST5_ID 0x0205
51 #define SPLAT_TASKQ_TEST5_NAME "order"
52 #define SPLAT_TASKQ_TEST5_DESC "Correct task ordering"
53
54 #define SPLAT_TASKQ_TEST6_ID 0x0206
55 #define SPLAT_TASKQ_TEST6_NAME "front"
56 #define SPLAT_TASKQ_TEST6_DESC "Correct ordering with TQ_FRONT flag"
57
58 #define SPLAT_TASKQ_TEST7_ID 0x0207
59 #define SPLAT_TASKQ_TEST7_NAME "recurse"
60 #define SPLAT_TASKQ_TEST7_DESC "Single task queue, recursive dispatch"
61
62 #define SPLAT_TASKQ_TEST8_ID 0x0208
63 #define SPLAT_TASKQ_TEST8_NAME "contention"
64 #define SPLAT_TASKQ_TEST8_DESC "1 queue, 100 threads, 131072 tasks"
65
66 #define SPLAT_TASKQ_ORDER_MAX 8
67 #define SPLAT_TASKQ_DEPTH_MAX 16
68
69
70 typedef struct splat_taskq_arg {
71 int flag;
72 int id;
73 atomic_t count;
74 int order[SPLAT_TASKQ_ORDER_MAX];
75 unsigned int depth;
76 taskq_t *tq;
77 taskq_ent_t *tqe;
78 spinlock_t lock;
79 struct file *file;
80 const char *name;
81 } splat_taskq_arg_t;
82
83 typedef struct splat_taskq_id {
84 int id;
85 splat_taskq_arg_t *arg;
86 } splat_taskq_id_t;
87
88 /*
89 * Create a taskq, queue a task, wait until task completes, ensure
90 * task ran properly, cleanup taskq.
91 */
92 static void
93 splat_taskq_test13_func(void *arg)
94 {
95 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
96
97 ASSERT(tq_arg);
98 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST1_NAME,
99 "Taskq '%s' function '%s' setting flag\n",
100 tq_arg->name, sym2str(splat_taskq_test13_func));
101 tq_arg->flag = 1;
102 }
103
104 static int
105 splat_taskq_test1_impl(struct file *file, void *arg, boolean_t prealloc)
106 {
107 taskq_t *tq;
108 taskqid_t id;
109 splat_taskq_arg_t tq_arg;
110 taskq_ent_t tqe;
111
112 taskq_init_ent(&tqe);
113
114 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
115 "Taskq '%s' creating (%s dispatch)\n",
116 SPLAT_TASKQ_TEST1_NAME,
117 prealloc ? "prealloc" : "dynamic");
118 if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, maxclsyspri,
119 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
120 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
121 "Taskq '%s' create failed\n",
122 SPLAT_TASKQ_TEST1_NAME);
123 return -EINVAL;
124 }
125
126 tq_arg.flag = 0;
127 tq_arg.id = 0;
128 tq_arg.file = file;
129 tq_arg.name = SPLAT_TASKQ_TEST1_NAME;
130
131 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
132 "Taskq '%s' function '%s' dispatching\n",
133 tq_arg.name, sym2str(splat_taskq_test13_func));
134 if (prealloc) {
135 taskq_dispatch_ent(tq, splat_taskq_test13_func,
136 &tq_arg, TQ_SLEEP, &tqe);
137 id = tqe.tqent_id;
138 } else {
139 id = taskq_dispatch(tq, splat_taskq_test13_func,
140 &tq_arg, TQ_SLEEP);
141 }
142
143 if (id == 0) {
144 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
145 "Taskq '%s' function '%s' dispatch failed\n",
146 tq_arg.name, sym2str(splat_taskq_test13_func));
147 taskq_destroy(tq);
148 return -EINVAL;
149 }
150
151 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
152 tq_arg.name);
153 taskq_wait(tq);
154 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
155 tq_arg.name);
156
157 taskq_destroy(tq);
158
159 return (tq_arg.flag) ? 0 : -EINVAL;
160 }
161
162 static int
163 splat_taskq_test1(struct file *file, void *arg)
164 {
165 int rc;
166
167 rc = splat_taskq_test1_impl(file, arg, B_FALSE);
168 if (rc)
169 return rc;
170
171 rc = splat_taskq_test1_impl(file, arg, B_TRUE);
172
173 return rc;
174 }
175
176 /*
177 * Create multiple taskq's, each with multiple tasks, wait until
178 * all tasks complete, ensure all tasks ran properly and in the
179 * correct order. Run order must be the same as the order submitted
180 * because we only have 1 thread per taskq. Finally cleanup the taskq.
181 */
182 static void
183 splat_taskq_test2_func1(void *arg)
184 {
185 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
186
187 ASSERT(tq_arg);
188 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
189 "Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
190 tq_arg->name, tq_arg->id,
191 sym2str(splat_taskq_test2_func1),
192 tq_arg->flag * 2, tq_arg->flag);
193 tq_arg->flag *= 2;
194 }
195
196 static void
197 splat_taskq_test2_func2(void *arg)
198 {
199 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
200
201 ASSERT(tq_arg);
202 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
203 "Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
204 tq_arg->name, tq_arg->id,
205 sym2str(splat_taskq_test2_func2),
206 tq_arg->flag + 1, tq_arg->flag);
207 tq_arg->flag += 1;
208 }
209
210 #define TEST2_TASKQS 8
211 #define TEST2_THREADS_PER_TASKQ 1
212
213 static int
214 splat_taskq_test2_impl(struct file *file, void *arg, boolean_t prealloc) {
215 taskq_t *tq[TEST2_TASKQS] = { NULL };
216 taskqid_t id;
217 splat_taskq_arg_t tq_args[TEST2_TASKQS];
218 taskq_ent_t *func1_tqes = NULL;
219 taskq_ent_t *func2_tqes = NULL;
220 int i, rc = 0;
221
222 func1_tqes = kmalloc(sizeof(*func1_tqes) * TEST2_TASKQS, GFP_KERNEL);
223 if (func1_tqes == NULL) {
224 rc = -ENOMEM;
225 goto out;
226 }
227
228 func2_tqes = kmalloc(sizeof(*func2_tqes) * TEST2_TASKQS, GFP_KERNEL);
229 if (func2_tqes == NULL) {
230 rc = -ENOMEM;
231 goto out;
232 }
233
234 for (i = 0; i < TEST2_TASKQS; i++) {
235 taskq_init_ent(&func1_tqes[i]);
236 taskq_init_ent(&func2_tqes[i]);
237
238 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
239 "Taskq '%s/%d' creating (%s dispatch)\n",
240 SPLAT_TASKQ_TEST2_NAME, i,
241 prealloc ? "prealloc" : "dynamic");
242 if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME,
243 TEST2_THREADS_PER_TASKQ,
244 maxclsyspri, 50, INT_MAX,
245 TASKQ_PREPOPULATE)) == NULL) {
246 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
247 "Taskq '%s/%d' create failed\n",
248 SPLAT_TASKQ_TEST2_NAME, i);
249 rc = -EINVAL;
250 break;
251 }
252
253 tq_args[i].flag = i;
254 tq_args[i].id = i;
255 tq_args[i].file = file;
256 tq_args[i].name = SPLAT_TASKQ_TEST2_NAME;
257
258 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
259 "Taskq '%s/%d' function '%s' dispatching\n",
260 tq_args[i].name, tq_args[i].id,
261 sym2str(splat_taskq_test2_func1));
262 if (prealloc) {
263 taskq_dispatch_ent(tq[i], splat_taskq_test2_func1,
264 &tq_args[i], TQ_SLEEP, &func1_tqes[i]);
265 id = func1_tqes[i].tqent_id;
266 } else {
267 id = taskq_dispatch(tq[i], splat_taskq_test2_func1,
268 &tq_args[i], TQ_SLEEP);
269 }
270
271 if (id == 0) {
272 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
273 "Taskq '%s/%d' function '%s' dispatch "
274 "failed\n", tq_args[i].name, tq_args[i].id,
275 sym2str(splat_taskq_test2_func1));
276 rc = -EINVAL;
277 break;
278 }
279
280 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
281 "Taskq '%s/%d' function '%s' dispatching\n",
282 tq_args[i].name, tq_args[i].id,
283 sym2str(splat_taskq_test2_func2));
284 if (prealloc) {
285 taskq_dispatch_ent(tq[i], splat_taskq_test2_func2,
286 &tq_args[i], TQ_SLEEP, &func2_tqes[i]);
287 id = func2_tqes[i].tqent_id;
288 } else {
289 id = taskq_dispatch(tq[i], splat_taskq_test2_func2,
290 &tq_args[i], TQ_SLEEP);
291 }
292
293 if (id == 0) {
294 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq "
295 "'%s/%d' function '%s' dispatch failed\n",
296 tq_args[i].name, tq_args[i].id,
297 sym2str(splat_taskq_test2_func2));
298 rc = -EINVAL;
299 break;
300 }
301 }
302
303 /* When rc is set we're effectively just doing cleanup here, so
304 * ignore new errors in that case. They just cause noise. */
305 for (i = 0; i < TEST2_TASKQS; i++) {
306 if (tq[i] != NULL) {
307 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
308 "Taskq '%s/%d' waiting\n",
309 tq_args[i].name, tq_args[i].id);
310 taskq_wait(tq[i]);
311 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
312 "Taskq '%s/%d; destroying\n",
313 tq_args[i].name, tq_args[i].id);
314
315 taskq_destroy(tq[i]);
316
317 if (!rc && tq_args[i].flag != ((i * 2) + 1)) {
318 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
319 "Taskq '%s/%d' processed tasks "
320 "out of order; %d != %d\n",
321 tq_args[i].name, tq_args[i].id,
322 tq_args[i].flag, i * 2 + 1);
323 rc = -EINVAL;
324 } else {
325 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
326 "Taskq '%s/%d' processed tasks "
327 "in the correct order; %d == %d\n",
328 tq_args[i].name, tq_args[i].id,
329 tq_args[i].flag, i * 2 + 1);
330 }
331 }
332 }
333 out:
334 if (func1_tqes)
335 kfree(func1_tqes);
336
337 if (func2_tqes)
338 kfree(func2_tqes);
339
340 return rc;
341 }
342
343 static int
344 splat_taskq_test2(struct file *file, void *arg) {
345 int rc;
346
347 rc = splat_taskq_test2_impl(file, arg, B_FALSE);
348 if (rc)
349 return rc;
350
351 rc = splat_taskq_test2_impl(file, arg, B_TRUE);
352
353 return rc;
354 }
355
356 /*
357 * Use the global system task queue with a single task, wait until task
358 * completes, ensure task ran properly.
359 */
360 static int
361 splat_taskq_test3_impl(struct file *file, void *arg, boolean_t prealloc)
362 {
363 taskqid_t id;
364 splat_taskq_arg_t tq_arg;
365 taskq_ent_t tqe;
366
367 taskq_init_ent(&tqe);
368
369 tq_arg.flag = 0;
370 tq_arg.id = 0;
371 tq_arg.file = file;
372 tq_arg.name = SPLAT_TASKQ_TEST3_NAME;
373
374 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
375 "Taskq '%s' function '%s' %s dispatch\n",
376 tq_arg.name, sym2str(splat_taskq_test13_func),
377 prealloc ? "prealloc" : "dynamic");
378 if (prealloc) {
379 taskq_dispatch_ent(system_taskq, splat_taskq_test13_func,
380 &tq_arg, TQ_SLEEP, &tqe);
381 id = tqe.tqent_id;
382 } else {
383 id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
384 &tq_arg, TQ_SLEEP);
385 }
386
387 if (id == 0) {
388 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
389 "Taskq '%s' function '%s' dispatch failed\n",
390 tq_arg.name, sym2str(splat_taskq_test13_func));
391 return -EINVAL;
392 }
393
394 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
395 tq_arg.name);
396 taskq_wait(system_taskq);
397
398 return (tq_arg.flag) ? 0 : -EINVAL;
399 }
400
401 static int
402 splat_taskq_test3(struct file *file, void *arg)
403 {
404 int rc;
405
406 rc = splat_taskq_test3_impl(file, arg, B_FALSE);
407 if (rc)
408 return rc;
409
410 rc = splat_taskq_test3_impl(file, arg, B_TRUE);
411
412 return rc;
413 }
414
415 /*
416 * Create a taskq and dispatch a large number of tasks to the queue.
417 * Then use taskq_wait() to block until all the tasks complete, then
418 * cross check that all the tasks ran by checking tg_arg->count which
419 * is incremented in the task function. Finally cleanup the taskq.
420 *
421 * First we try with a large 'maxalloc' value, then we try with a small one.
422 * We should not drop tasks when TQ_SLEEP is used in taskq_dispatch(), even
423 * if the number of pending tasks is above maxalloc.
424 */
425 static void
426 splat_taskq_test4_func(void *arg)
427 {
428 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
429 ASSERT(tq_arg);
430
431 atomic_inc(&tq_arg->count);
432 }
433
434 static int
435 splat_taskq_test4_common(struct file *file, void *arg, int minalloc,
436 int maxalloc, int nr_tasks, boolean_t prealloc)
437 {
438 taskq_t *tq;
439 taskqid_t id;
440 splat_taskq_arg_t tq_arg;
441 taskq_ent_t *tqes;
442 int i, j, rc = 0;
443
444 tqes = kmalloc(sizeof(*tqes) * nr_tasks, GFP_KERNEL);
445 if (tqes == NULL)
446 return -ENOMEM;
447
448 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
449 "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n",
450 SPLAT_TASKQ_TEST4_NAME,
451 prealloc ? "prealloc" : "dynamic",
452 minalloc, maxalloc, nr_tasks);
453 if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, maxclsyspri,
454 minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
455 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
456 "Taskq '%s' create failed\n",
457 SPLAT_TASKQ_TEST4_NAME);
458 rc = -EINVAL;
459 goto out_free;
460 }
461
462 tq_arg.file = file;
463 tq_arg.name = SPLAT_TASKQ_TEST4_NAME;
464
465 for (i = 1; i <= nr_tasks; i *= 2) {
466 atomic_set(&tq_arg.count, 0);
467 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
468 "Taskq '%s' function '%s' dispatched %d times\n",
469 tq_arg.name, sym2str(splat_taskq_test4_func), i);
470
471 for (j = 0; j < i; j++) {
472 taskq_init_ent(&tqes[j]);
473
474 if (prealloc) {
475 taskq_dispatch_ent(tq, splat_taskq_test4_func,
476 &tq_arg, TQ_SLEEP, &tqes[j]);
477 id = tqes[j].tqent_id;
478 } else {
479 id = taskq_dispatch(tq, splat_taskq_test4_func,
480 &tq_arg, TQ_SLEEP);
481 }
482
483 if (id == 0) {
484 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
485 "Taskq '%s' function '%s' dispatch "
486 "%d failed\n", tq_arg.name,
487 sym2str(splat_taskq_test4_func), j);
488 rc = -EINVAL;
489 goto out;
490 }
491 }
492
493 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
494 "waiting for %d dispatches\n", tq_arg.name, i);
495 taskq_wait(tq);
496 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
497 "%d/%d dispatches finished\n", tq_arg.name,
498 atomic_read(&tq_arg.count), i);
499 if (atomic_read(&tq_arg.count) != i) {
500 rc = -ERANGE;
501 goto out;
502
503 }
504 }
505 out:
506 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' destroying\n",
507 tq_arg.name);
508 taskq_destroy(tq);
509
510 out_free:
511 kfree(tqes);
512
513 return rc;
514 }
515
516 static int
517 splat_taskq_test4_impl(struct file *file, void *arg, boolean_t prealloc)
518 {
519 int rc;
520
521 rc = splat_taskq_test4_common(file, arg, 50, INT_MAX, 1024, prealloc);
522 if (rc)
523 return rc;
524
525 rc = splat_taskq_test4_common(file, arg, 1, 1, 32, prealloc);
526
527 return rc;
528 }
529
530 static int
531 splat_taskq_test4(struct file *file, void *arg)
532 {
533 int rc;
534
535 rc = splat_taskq_test4_impl(file, arg, B_FALSE);
536 if (rc)
537 return rc;
538
539 rc = splat_taskq_test4_impl(file, arg, B_TRUE);
540
541 return rc;
542 }
543
544 /*
545 * Create a taskq and dispatch a specific sequence of tasks carefully
546 * crafted to validate the order in which tasks are processed. When
547 * there are multiple worker threads each thread will process the
548 * next pending task as soon as it completes its current task. This
549 * means that tasks do not strictly complete in order in which they
550 * were dispatched (increasing task id). This is fine but we need to
551 * verify that taskq_wait_id() blocks until the passed task id and all
552 * lower task ids complete. We do this by dispatching the following
553 * specific sequence of tasks each of which block for N time units.
554 * We then use taskq_wait_id() to unblock at specific task id and
555 * verify the only the expected task ids have completed and in the
556 * correct order. The two cases of interest are:
557 *
558 * 1) Task ids larger than the waited for task id can run and
559 * complete as long as there is an available worker thread.
560 * 2) All task ids lower than the waited one must complete before
561 * unblocking even if the waited task id itself has completed.
562 *
563 * The following table shows each task id and how they will be
564 * scheduled. Each rows represent one time unit and each column
565 * one of the three worker threads. The places taskq_wait_id()
566 * must unblock for a specific id are identified as well as the
567 * task ids which must have completed and their order.
568 *
569 * +-----+ <--- taskq_wait_id(tq, 8) unblocks
570 * | | Required Completion Order: 1,2,4,5,3,8,6,7
571 * +-----+ |
572 * | | |
573 * | | +-----+
574 * | | | 8 |
575 * | | +-----+ <--- taskq_wait_id(tq, 3) unblocks
576 * | | 7 | | Required Completion Order: 1,2,4,5,3
577 * | +-----+ |
578 * | 6 | | |
579 * +-----+ | |
580 * | | 5 | |
581 * | +-----+ |
582 * | 4 | | |
583 * +-----+ | |
584 * | 1 | 2 | 3 |
585 * +-----+-----+-----+
586 *
587 */
588 static void
589 splat_taskq_test5_func(void *arg)
590 {
591 splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg;
592 splat_taskq_arg_t *tq_arg = tq_id->arg;
593 int factor;
594
595 /* Delays determined by above table */
596 switch (tq_id->id) {
597 default: factor = 0; break;
598 case 1: case 8: factor = 1; break;
599 case 2: case 4: case 5: factor = 2; break;
600 case 6: case 7: factor = 4; break;
601 case 3: factor = 5; break;
602 }
603
604 msleep(factor * 100);
605 splat_vprint(tq_arg->file, tq_arg->name,
606 "Taskqid %d complete for taskq '%s'\n",
607 tq_id->id, tq_arg->name);
608
609 spin_lock(&tq_arg->lock);
610 tq_arg->order[tq_arg->flag] = tq_id->id;
611 tq_arg->flag++;
612 spin_unlock(&tq_arg->lock);
613 }
614
615 static int
616 splat_taskq_test_order(splat_taskq_arg_t *tq_arg, int *order)
617 {
618 int i, j;
619
620 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
621 if (tq_arg->order[i] != order[i]) {
622 splat_vprint(tq_arg->file, tq_arg->name,
623 "Taskq '%s' incorrect completion "
624 "order\n", tq_arg->name);
625 splat_vprint(tq_arg->file, tq_arg->name,
626 "%s", "Expected { ");
627
628 for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++)
629 splat_print(tq_arg->file, "%d ", order[j]);
630
631 splat_print(tq_arg->file, "%s", "}\n");
632 splat_vprint(tq_arg->file, tq_arg->name,
633 "%s", "Got { ");
634
635 for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++)
636 splat_print(tq_arg->file, "%d ",
637 tq_arg->order[j]);
638
639 splat_print(tq_arg->file, "%s", "}\n");
640 return -EILSEQ;
641 }
642 }
643
644 splat_vprint(tq_arg->file, tq_arg->name,
645 "Taskq '%s' validated correct completion order\n",
646 tq_arg->name);
647
648 return 0;
649 }
650
651 static int
652 splat_taskq_test5_impl(struct file *file, void *arg, boolean_t prealloc)
653 {
654 taskq_t *tq;
655 taskqid_t id;
656 splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX];
657 splat_taskq_arg_t tq_arg;
658 int order1[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,0,0,0 };
659 int order2[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,8,6,7 };
660 taskq_ent_t tqes[SPLAT_TASKQ_ORDER_MAX];
661 int i, rc = 0;
662
663 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
664 "Taskq '%s' creating (%s dispatch)\n",
665 SPLAT_TASKQ_TEST5_NAME,
666 prealloc ? "prealloc" : "dynamic");
667 if ((tq = taskq_create(SPLAT_TASKQ_TEST5_NAME, 3, maxclsyspri,
668 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
669 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
670 "Taskq '%s' create failed\n",
671 SPLAT_TASKQ_TEST5_NAME);
672 return -EINVAL;
673 }
674
675 tq_arg.flag = 0;
676 memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX);
677 spin_lock_init(&tq_arg.lock);
678 tq_arg.file = file;
679 tq_arg.name = SPLAT_TASKQ_TEST5_NAME;
680
681 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
682 taskq_init_ent(&tqes[i]);
683
684 tq_id[i].id = i + 1;
685 tq_id[i].arg = &tq_arg;
686
687 if (prealloc) {
688 taskq_dispatch_ent(tq, splat_taskq_test5_func,
689 &tq_id[i], TQ_SLEEP, &tqes[i]);
690 id = tqes[i].tqent_id;
691 } else {
692 id = taskq_dispatch(tq, splat_taskq_test5_func,
693 &tq_id[i], TQ_SLEEP);
694 }
695
696 if (id == 0) {
697 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
698 "Taskq '%s' function '%s' dispatch failed\n",
699 tq_arg.name, sym2str(splat_taskq_test5_func));
700 rc = -EINVAL;
701 goto out;
702 }
703
704 if (tq_id[i].id != id) {
705 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
706 "Taskq '%s' expected taskqid %d got %d\n",
707 tq_arg.name, (int)tq_id[i].id, (int)id);
708 rc = -EINVAL;
709 goto out;
710 }
711 }
712
713 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' "
714 "waiting for taskqid %d completion\n", tq_arg.name, 3);
715 taskq_wait_id(tq, 3);
716 if ((rc = splat_taskq_test_order(&tq_arg, order1)))
717 goto out;
718
719 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' "
720 "waiting for taskqid %d completion\n", tq_arg.name, 8);
721 taskq_wait_id(tq, 8);
722 rc = splat_taskq_test_order(&tq_arg, order2);
723
724 out:
725 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
726 "Taskq '%s' destroying\n", tq_arg.name);
727 taskq_destroy(tq);
728
729 return rc;
730 }
731
732 static int
733 splat_taskq_test5(struct file *file, void *arg)
734 {
735 int rc;
736
737 rc = splat_taskq_test5_impl(file, arg, B_FALSE);
738 if (rc)
739 return rc;
740
741 rc = splat_taskq_test5_impl(file, arg, B_TRUE);
742
743 return rc;
744 }
745
746 /*
747 * Create a single task queue with three threads. Dispatch 8 tasks,
748 * setting TQ_FRONT on only the last three. Sleep after
749 * dispatching tasks 1-3 to ensure they will run and hold the threads
750 * busy while we dispatch the remaining tasks. Verify that tasks 6-8
751 * run before task 4-5.
752 *
753 * The following table shows each task id and how they will be
754 * scheduled. Each rows represent one time unit and each column
755 * one of the three worker threads.
756 *
757 * +-----+
758 * | |
759 * +-----+ |
760 * | | 5 +-----+
761 * | | | |
762 * | +-----| |
763 * | 4 | | |
764 * +-----+ | 8 |
765 * | | | |
766 * | | 7 +-----+
767 * | | | |
768 * | |-----+ |
769 * | 6 | | |
770 * +-----+ | |
771 * | | | |
772 * | 1 | 2 | 3 |
773 * +-----+-----+-----+
774 *
775 */
776 static void
777 splat_taskq_test6_func(void *arg)
778 {
779 splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg;
780 splat_taskq_arg_t *tq_arg = tq_id->arg;
781 int factor;
782
783 /* Delays determined by above table */
784 switch (tq_id->id) {
785 default: factor = 0; break;
786 case 1: factor = 2; break;
787 case 2: case 4: case 5: factor = 4; break;
788 case 6: case 7: case 8: factor = 5; break;
789 case 3: factor = 6; break;
790 }
791
792 msleep(factor * 100);
793
794 splat_vprint(tq_arg->file, tq_arg->name,
795 "Taskqid %d complete for taskq '%s'\n",
796 tq_id->id, tq_arg->name);
797
798 spin_lock(&tq_arg->lock);
799 tq_arg->order[tq_arg->flag] = tq_id->id;
800 tq_arg->flag++;
801 spin_unlock(&tq_arg->lock);
802 }
803
804 static int
805 splat_taskq_test6_impl(struct file *file, void *arg, boolean_t prealloc)
806 {
807 taskq_t *tq;
808 taskqid_t id;
809 splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX];
810 splat_taskq_arg_t tq_arg;
811 int order[SPLAT_TASKQ_ORDER_MAX] = { 1,2,3,6,7,8,4,5 };
812 taskq_ent_t tqes[SPLAT_TASKQ_ORDER_MAX];
813 int i, rc = 0;
814 uint_t tflags;
815
816 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
817 "Taskq '%s' creating (%s dispatch)\n",
818 SPLAT_TASKQ_TEST6_NAME,
819 prealloc ? "prealloc" : "dynamic");
820 if ((tq = taskq_create(SPLAT_TASKQ_TEST6_NAME, 3, maxclsyspri,
821 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
822 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
823 "Taskq '%s' create failed\n",
824 SPLAT_TASKQ_TEST6_NAME);
825 return -EINVAL;
826 }
827
828 tq_arg.flag = 0;
829 memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX);
830 spin_lock_init(&tq_arg.lock);
831 tq_arg.file = file;
832 tq_arg.name = SPLAT_TASKQ_TEST6_NAME;
833
834 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
835 taskq_init_ent(&tqes[i]);
836
837 tq_id[i].id = i + 1;
838 tq_id[i].arg = &tq_arg;
839 tflags = TQ_SLEEP;
840 if (i > 4)
841 tflags |= TQ_FRONT;
842
843 if (prealloc) {
844 taskq_dispatch_ent(tq, splat_taskq_test6_func,
845 &tq_id[i], tflags, &tqes[i]);
846 id = tqes[i].tqent_id;
847 } else {
848 id = taskq_dispatch(tq, splat_taskq_test6_func,
849 &tq_id[i], tflags);
850 }
851
852 if (id == 0) {
853 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
854 "Taskq '%s' function '%s' dispatch failed\n",
855 tq_arg.name, sym2str(splat_taskq_test6_func));
856 rc = -EINVAL;
857 goto out;
858 }
859
860 if (tq_id[i].id != id) {
861 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
862 "Taskq '%s' expected taskqid %d got %d\n",
863 tq_arg.name, (int)tq_id[i].id, (int)id);
864 rc = -EINVAL;
865 goto out;
866 }
867 /* Sleep to let tasks 1-3 start executing. */
868 if ( i == 2 )
869 msleep(100);
870 }
871
872 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' "
873 "waiting for taskqid %d completion\n", tq_arg.name,
874 SPLAT_TASKQ_ORDER_MAX);
875 taskq_wait_id(tq, SPLAT_TASKQ_ORDER_MAX);
876 rc = splat_taskq_test_order(&tq_arg, order);
877
878 out:
879 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
880 "Taskq '%s' destroying\n", tq_arg.name);
881 taskq_destroy(tq);
882
883 return rc;
884 }
885
886 static int
887 splat_taskq_test6(struct file *file, void *arg)
888 {
889 int rc;
890
891 rc = splat_taskq_test6_impl(file, arg, B_FALSE);
892 if (rc)
893 return rc;
894
895 rc = splat_taskq_test6_impl(file, arg, B_TRUE);
896
897 return rc;
898 }
899
900 static void
901 splat_taskq_test7_func(void *arg)
902 {
903 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
904 taskqid_t id;
905
906 ASSERT(tq_arg);
907
908 if (tq_arg->depth >= SPLAT_TASKQ_DEPTH_MAX)
909 return;
910
911 tq_arg->depth++;
912
913 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST7_NAME,
914 "Taskq '%s' function '%s' dispatching (depth = %u)\n",
915 tq_arg->name, sym2str(splat_taskq_test7_func),
916 tq_arg->depth);
917
918 if (tq_arg->tqe) {
919 VERIFY(taskq_empty_ent(tq_arg->tqe));
920 taskq_dispatch_ent(tq_arg->tq, splat_taskq_test7_func,
921 tq_arg, TQ_SLEEP, tq_arg->tqe);
922 id = tq_arg->tqe->tqent_id;
923 } else {
924 id = taskq_dispatch(tq_arg->tq, splat_taskq_test7_func,
925 tq_arg, TQ_SLEEP);
926 }
927
928 if (id == 0) {
929 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST7_NAME,
930 "Taskq '%s' function '%s' dispatch failed "
931 "(depth = %u)\n", tq_arg->name,
932 sym2str(splat_taskq_test7_func), tq_arg->depth);
933 tq_arg->flag = -EINVAL;
934 return;
935 }
936 }
937
938 static int
939 splat_taskq_test7_impl(struct file *file, void *arg, boolean_t prealloc)
940 {
941 taskq_t *tq;
942 taskq_ent_t tqe;
943 splat_taskq_arg_t tq_arg;
944
945 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
946 "Taskq '%s' creating (%s dispatch)\n",
947 SPLAT_TASKQ_TEST7_NAME,
948 prealloc ? "prealloc" : "dynamic");
949 if ((tq = taskq_create(SPLAT_TASKQ_TEST7_NAME, 1, maxclsyspri,
950 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
951 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
952 "Taskq '%s' create failed\n",
953 SPLAT_TASKQ_TEST7_NAME);
954 return -EINVAL;
955 }
956
957 tq_arg.depth = 0;
958 tq_arg.flag = 0;
959 tq_arg.id = 0;
960 tq_arg.file = file;
961 tq_arg.name = SPLAT_TASKQ_TEST7_NAME;
962 tq_arg.tq = tq;
963
964 if (prealloc) {
965 taskq_init_ent(&tqe);
966 tq_arg.tqe = &tqe;
967 } else {
968 tq_arg.tqe = NULL;
969 }
970
971 splat_taskq_test7_func(&tq_arg);
972
973 if (tq_arg.flag == 0) {
974 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
975 "Taskq '%s' waiting\n", tq_arg.name);
976 taskq_wait_id(tq, SPLAT_TASKQ_DEPTH_MAX);
977 }
978
979 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
980 "Taskq '%s' destroying\n", tq_arg.name);
981 taskq_destroy(tq);
982
983 return tq_arg.depth == SPLAT_TASKQ_DEPTH_MAX ? 0 : -EINVAL;
984 }
985
986 static int
987 splat_taskq_test7(struct file *file, void *arg)
988 {
989 int rc;
990
991 rc = splat_taskq_test7_impl(file, arg, B_FALSE);
992 if (rc)
993 return rc;
994
995 rc = splat_taskq_test7_impl(file, arg, B_TRUE);
996
997 return rc;
998 }
999
1000 /*
1001 * Create a taskq with 100 threads and dispatch a huge number of trivial
1002 * tasks to generate contention on tq->tq_lock. This test should always
1003 * pass. The purpose is to provide a benchmark for measuring the
1004 * effectiveness of taskq optimizations.
1005 */
1006 static void
1007 splat_taskq_test8_func(void *arg)
1008 {
1009 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
1010 ASSERT(tq_arg);
1011
1012 atomic_inc(&tq_arg->count);
1013 }
1014
1015 #define TEST8_NUM_TASKS 0x20000
1016 #define TEST8_THREADS_PER_TASKQ 100
1017
1018 static int
1019 splat_taskq_test8_common(struct file *file, void *arg, int minalloc,
1020 int maxalloc)
1021 {
1022 taskq_t *tq;
1023 taskqid_t id;
1024 splat_taskq_arg_t tq_arg;
1025 taskq_ent_t **tqes;
1026 int i, j, rc = 0;
1027
1028 tqes = vmalloc(sizeof(*tqes) * TEST8_NUM_TASKS);
1029 if (tqes == NULL)
1030 return -ENOMEM;
1031 memset(tqes, 0, sizeof(*tqes) * TEST8_NUM_TASKS);
1032
1033 splat_vprint(file, SPLAT_TASKQ_TEST8_NAME,
1034 "Taskq '%s' creating (%d/%d/%d)\n",
1035 SPLAT_TASKQ_TEST8_NAME,
1036 minalloc, maxalloc, TEST8_NUM_TASKS);
1037 if ((tq = taskq_create(SPLAT_TASKQ_TEST8_NAME, TEST8_THREADS_PER_TASKQ,
1038 maxclsyspri, minalloc, maxalloc,
1039 TASKQ_PREPOPULATE)) == NULL) {
1040 splat_vprint(file, SPLAT_TASKQ_TEST8_NAME,
1041 "Taskq '%s' create failed\n",
1042 SPLAT_TASKQ_TEST8_NAME);
1043 rc = -EINVAL;
1044 goto out_free;
1045 }
1046
1047 tq_arg.file = file;
1048 tq_arg.name = SPLAT_TASKQ_TEST8_NAME;
1049
1050 atomic_set(&tq_arg.count, 0);
1051 for (i = 0; i < TEST8_NUM_TASKS; i++) {
1052 tqes[i] = kmalloc(sizeof(taskq_ent_t), GFP_KERNEL);
1053 if (tqes[i] == NULL) {
1054 rc = -ENOMEM;
1055 goto out;
1056 }
1057 taskq_init_ent(tqes[i]);
1058
1059 taskq_dispatch_ent(tq, splat_taskq_test8_func,
1060 &tq_arg, TQ_SLEEP, tqes[i]);
1061
1062 id = tqes[i]->tqent_id;
1063
1064 if (id == 0) {
1065 splat_vprint(file, SPLAT_TASKQ_TEST8_NAME,
1066 "Taskq '%s' function '%s' dispatch "
1067 "%d failed\n", tq_arg.name,
1068 sym2str(splat_taskq_test8_func), i);
1069 rc = -EINVAL;
1070 goto out;
1071 }
1072 }
1073
1074 splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' "
1075 "waiting for %d dispatches\n", tq_arg.name,
1076 TEST8_NUM_TASKS);
1077 taskq_wait(tq);
1078 splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' "
1079 "%d/%d dispatches finished\n", tq_arg.name,
1080 atomic_read(&tq_arg.count), TEST8_NUM_TASKS);
1081
1082 if (atomic_read(&tq_arg.count) != TEST8_NUM_TASKS)
1083 rc = -ERANGE;
1084
1085 out:
1086 splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' destroying\n",
1087 tq_arg.name);
1088 taskq_destroy(tq);
1089 out_free:
1090 for (j = 0; j < TEST8_NUM_TASKS && tqes[j] != NULL; j++)
1091 kfree(tqes[j]);
1092 vfree(tqes);
1093
1094 return rc;
1095 }
1096
1097 static int
1098 splat_taskq_test8(struct file *file, void *arg)
1099 {
1100 int rc;
1101
1102 rc = splat_taskq_test8_common(file, arg, 1, 100);
1103
1104 return rc;
1105 }
1106
1107 splat_subsystem_t *
1108 splat_taskq_init(void)
1109 {
1110 splat_subsystem_t *sub;
1111
1112 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
1113 if (sub == NULL)
1114 return NULL;
1115
1116 memset(sub, 0, sizeof(*sub));
1117 strncpy(sub->desc.name, SPLAT_TASKQ_NAME, SPLAT_NAME_SIZE);
1118 strncpy(sub->desc.desc, SPLAT_TASKQ_DESC, SPLAT_DESC_SIZE);
1119 INIT_LIST_HEAD(&sub->subsystem_list);
1120 INIT_LIST_HEAD(&sub->test_list);
1121 spin_lock_init(&sub->test_lock);
1122 sub->desc.id = SPLAT_SUBSYSTEM_TASKQ;
1123
1124 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST1_NAME, SPLAT_TASKQ_TEST1_DESC,
1125 SPLAT_TASKQ_TEST1_ID, splat_taskq_test1);
1126 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST2_NAME, SPLAT_TASKQ_TEST2_DESC,
1127 SPLAT_TASKQ_TEST2_ID, splat_taskq_test2);
1128 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST3_NAME, SPLAT_TASKQ_TEST3_DESC,
1129 SPLAT_TASKQ_TEST3_ID, splat_taskq_test3);
1130 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST4_NAME, SPLAT_TASKQ_TEST4_DESC,
1131 SPLAT_TASKQ_TEST4_ID, splat_taskq_test4);
1132 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST5_NAME, SPLAT_TASKQ_TEST5_DESC,
1133 SPLAT_TASKQ_TEST5_ID, splat_taskq_test5);
1134 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST6_NAME, SPLAT_TASKQ_TEST6_DESC,
1135 SPLAT_TASKQ_TEST6_ID, splat_taskq_test6);
1136 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST7_NAME, SPLAT_TASKQ_TEST7_DESC,
1137 SPLAT_TASKQ_TEST7_ID, splat_taskq_test7);
1138 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST8_NAME, SPLAT_TASKQ_TEST8_DESC,
1139 SPLAT_TASKQ_TEST8_ID, splat_taskq_test8);
1140
1141 return sub;
1142 }
1143
1144 void
1145 splat_taskq_fini(splat_subsystem_t *sub)
1146 {
1147 ASSERT(sub);
1148 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST8_ID);
1149 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST7_ID);
1150 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST6_ID);
1151 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST5_ID);
1152 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST4_ID);
1153 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST3_ID);
1154 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST2_ID);
1155 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST1_ID);
1156
1157 kfree(sub);
1158 }
1159
1160 int
1161 splat_taskq_id(void) {
1162 return SPLAT_SUBSYSTEM_TASKQ;
1163 }