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>.
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://github.com/behlendorf/spl/>.
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.
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
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 \*****************************************************************************/
27 #include <sys/taskq.h>
29 #include "splat-internal.h"
31 #define SPLAT_TASKQ_NAME "taskq"
32 #define SPLAT_TASKQ_DESC "Kernel Task Queue Tests"
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"
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"
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"
46 #define SPLAT_TASKQ_TEST4_ID 0x0204
47 #define SPLAT_TASKQ_TEST4_NAME "wait"
48 #define SPLAT_TASKQ_TEST4_DESC "Multiple task waiting"
50 #define SPLAT_TASKQ_TEST5_ID 0x0205
51 #define SPLAT_TASKQ_TEST5_NAME "order"
52 #define SPLAT_TASKQ_TEST5_DESC "Correct task ordering"
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"
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"
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"
66 #define SPLAT_TASKQ_ORDER_MAX 8
67 #define SPLAT_TASKQ_DEPTH_MAX 16
70 typedef struct splat_taskq_arg
{
74 int order
[SPLAT_TASKQ_ORDER_MAX
];
83 typedef struct splat_taskq_id
{
85 splat_taskq_arg_t
*arg
;
89 * Create a taskq, queue a task, wait until task completes, ensure
90 * task ran properly, cleanup taskq.
93 splat_taskq_test13_func(void *arg
)
95 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)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
));
105 splat_taskq_test1_impl(struct file
*file
, void *arg
, boolean_t prealloc
)
109 splat_taskq_arg_t tq_arg
;
112 taskq_init_ent(&tqe
);
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
);
129 tq_arg
.name
= SPLAT_TASKQ_TEST1_NAME
;
131 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
,
132 "Taskq '%s' function '%s' dispatching\n",
133 tq_arg
.name
, sym2str(splat_taskq_test13_func
));
135 taskq_dispatch_ent(tq
, splat_taskq_test13_func
,
136 &tq_arg
, TQ_SLEEP
, &tqe
);
139 id
= taskq_dispatch(tq
, splat_taskq_test13_func
,
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
));
151 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
, "Taskq '%s' waiting\n",
154 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
, "Taskq '%s' destroying\n",
159 return (tq_arg
.flag
) ? 0 : -EINVAL
;
163 splat_taskq_test1(struct file
*file
, void *arg
)
167 rc
= splat_taskq_test1_impl(file
, arg
, B_FALSE
);
171 rc
= splat_taskq_test1_impl(file
, arg
, B_TRUE
);
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.
183 splat_taskq_test2_func1(void *arg
)
185 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)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
);
197 splat_taskq_test2_func2(void *arg
)
199 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)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
);
210 #define TEST2_TASKQS 8
211 #define TEST2_THREADS_PER_TASKQ 1
214 splat_taskq_test2_impl(struct file
*file
, void *arg
, boolean_t prealloc
) {
215 taskq_t
*tq
[TEST2_TASKQS
] = { NULL
};
217 splat_taskq_arg_t tq_args
[TEST2_TASKQS
];
218 taskq_ent_t
*func1_tqes
= NULL
;
219 taskq_ent_t
*func2_tqes
= NULL
;
222 func1_tqes
= kmalloc(sizeof(*func1_tqes
) * TEST2_TASKQS
, GFP_KERNEL
);
223 if (func1_tqes
== NULL
) {
228 func2_tqes
= kmalloc(sizeof(*func2_tqes
) * TEST2_TASKQS
, GFP_KERNEL
);
229 if (func2_tqes
== NULL
) {
234 for (i
= 0; i
< TEST2_TASKQS
; i
++) {
235 taskq_init_ent(&func1_tqes
[i
]);
236 taskq_init_ent(&func2_tqes
[i
]);
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
);
255 tq_args
[i
].file
= file
;
256 tq_args
[i
].name
= SPLAT_TASKQ_TEST2_NAME
;
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
));
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
;
267 id
= taskq_dispatch(tq
[i
], splat_taskq_test2_func1
,
268 &tq_args
[i
], TQ_SLEEP
);
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
));
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
));
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
;
289 id
= taskq_dispatch(tq
[i
], splat_taskq_test2_func2
,
290 &tq_args
[i
], TQ_SLEEP
);
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
));
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
++) {
307 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
308 "Taskq '%s/%d' waiting\n",
309 tq_args
[i
].name
, tq_args
[i
].id
);
311 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
312 "Taskq '%s/%d; destroying\n",
313 tq_args
[i
].name
, tq_args
[i
].id
);
315 taskq_destroy(tq
[i
]);
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);
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);
344 splat_taskq_test2(struct file
*file
, void *arg
) {
347 rc
= splat_taskq_test2_impl(file
, arg
, B_FALSE
);
351 rc
= splat_taskq_test2_impl(file
, arg
, B_TRUE
);
357 * Use the global system task queue with a single task, wait until task
358 * completes, ensure task ran properly.
361 splat_taskq_test3_impl(struct file
*file
, void *arg
, boolean_t prealloc
)
364 splat_taskq_arg_t tq_arg
;
367 taskq_init_ent(&tqe
);
372 tq_arg
.name
= SPLAT_TASKQ_TEST3_NAME
;
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");
379 taskq_dispatch_ent(system_taskq
, splat_taskq_test13_func
,
380 &tq_arg
, TQ_SLEEP
, &tqe
);
383 id
= taskq_dispatch(system_taskq
, splat_taskq_test13_func
,
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
));
394 splat_vprint(file
, SPLAT_TASKQ_TEST3_NAME
, "Taskq '%s' waiting\n",
396 taskq_wait(system_taskq
);
398 return (tq_arg
.flag
) ? 0 : -EINVAL
;
402 splat_taskq_test3(struct file
*file
, void *arg
)
406 rc
= splat_taskq_test3_impl(file
, arg
, B_FALSE
);
410 rc
= splat_taskq_test3_impl(file
, arg
, B_TRUE
);
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.
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.
426 splat_taskq_test4_func(void *arg
)
428 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)arg
;
431 atomic_inc(&tq_arg
->count
);
435 splat_taskq_test4_common(struct file
*file
, void *arg
, int minalloc
,
436 int maxalloc
, int nr_tasks
, boolean_t prealloc
)
440 splat_taskq_arg_t tq_arg
;
444 tqes
= kmalloc(sizeof(*tqes
) * nr_tasks
, GFP_KERNEL
);
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
);
463 tq_arg
.name
= SPLAT_TASKQ_TEST4_NAME
;
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
);
471 for (j
= 0; j
< i
; j
++) {
472 taskq_init_ent(&tqes
[j
]);
475 taskq_dispatch_ent(tq
, splat_taskq_test4_func
,
476 &tq_arg
, TQ_SLEEP
, &tqes
[j
]);
477 id
= tqes
[j
].tqent_id
;
479 id
= taskq_dispatch(tq
, splat_taskq_test4_func
,
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
);
493 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
, "Taskq '%s' "
494 "waiting for %d dispatches\n", tq_arg
.name
, i
);
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
) {
506 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
, "Taskq '%s' destroying\n",
517 splat_taskq_test4_impl(struct file
*file
, void *arg
, boolean_t prealloc
)
521 rc
= splat_taskq_test4_common(file
, arg
, 50, INT_MAX
, 1024, prealloc
);
525 rc
= splat_taskq_test4_common(file
, arg
, 1, 1, 32, prealloc
);
531 splat_taskq_test4(struct file
*file
, void *arg
)
535 rc
= splat_taskq_test4_impl(file
, arg
, B_FALSE
);
539 rc
= splat_taskq_test4_impl(file
, arg
, B_TRUE
);
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:
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.
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.
569 * +-----+ <--- taskq_wait_id(tq, 8) unblocks
570 * | | Required Completion Order: 1,2,4,5,3,8,6,7
575 * | | +-----+ <--- taskq_wait_id(tq, 3) unblocks
576 * | | 7 | | Required Completion Order: 1,2,4,5,3
585 * +-----+-----+-----+
589 splat_taskq_test5_func(void *arg
)
591 splat_taskq_id_t
*tq_id
= (splat_taskq_id_t
*)arg
;
592 splat_taskq_arg_t
*tq_arg
= tq_id
->arg
;
595 /* Delays determined by above table */
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;
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
);
609 spin_lock(&tq_arg
->lock
);
610 tq_arg
->order
[tq_arg
->flag
] = tq_id
->id
;
612 spin_unlock(&tq_arg
->lock
);
616 splat_taskq_test_order(splat_taskq_arg_t
*tq_arg
, int *order
)
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 { ");
628 for (j
= 0; j
< SPLAT_TASKQ_ORDER_MAX
; j
++)
629 splat_print(tq_arg
->file
, "%d ", order
[j
]);
631 splat_print(tq_arg
->file
, "%s", "}\n");
632 splat_vprint(tq_arg
->file
, tq_arg
->name
,
635 for (j
= 0; j
< SPLAT_TASKQ_ORDER_MAX
; j
++)
636 splat_print(tq_arg
->file
, "%d ",
639 splat_print(tq_arg
->file
, "%s", "}\n");
644 splat_vprint(tq_arg
->file
, tq_arg
->name
,
645 "Taskq '%s' validated correct completion order\n",
652 splat_taskq_test5_impl(struct file
*file
, void *arg
, boolean_t prealloc
)
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
];
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
);
676 memset(&tq_arg
.order
, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX
);
677 spin_lock_init(&tq_arg
.lock
);
679 tq_arg
.name
= SPLAT_TASKQ_TEST5_NAME
;
681 for (i
= 0; i
< SPLAT_TASKQ_ORDER_MAX
; i
++) {
682 taskq_init_ent(&tqes
[i
]);
685 tq_id
[i
].arg
= &tq_arg
;
688 taskq_dispatch_ent(tq
, splat_taskq_test5_func
,
689 &tq_id
[i
], TQ_SLEEP
, &tqes
[i
]);
690 id
= tqes
[i
].tqent_id
;
692 id
= taskq_dispatch(tq
, splat_taskq_test5_func
,
693 &tq_id
[i
], TQ_SLEEP
);
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
));
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
);
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
)))
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
);
725 splat_vprint(file
, SPLAT_TASKQ_TEST5_NAME
,
726 "Taskq '%s' destroying\n", tq_arg
.name
);
733 splat_taskq_test5(struct file
*file
, void *arg
)
737 rc
= splat_taskq_test5_impl(file
, arg
, B_FALSE
);
741 rc
= splat_taskq_test5_impl(file
, arg
, B_TRUE
);
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.
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.
773 * +-----+-----+-----+
777 splat_taskq_test6_func(void *arg
)
779 splat_taskq_id_t
*tq_id
= (splat_taskq_id_t
*)arg
;
780 splat_taskq_arg_t
*tq_arg
= tq_id
->arg
;
783 /* Delays determined by above table */
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;
792 msleep(factor
* 100);
794 splat_vprint(tq_arg
->file
, tq_arg
->name
,
795 "Taskqid %d complete for taskq '%s'\n",
796 tq_id
->id
, tq_arg
->name
);
798 spin_lock(&tq_arg
->lock
);
799 tq_arg
->order
[tq_arg
->flag
] = tq_id
->id
;
801 spin_unlock(&tq_arg
->lock
);
805 splat_taskq_test6_impl(struct file
*file
, void *arg
, boolean_t prealloc
)
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
];
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
);
829 memset(&tq_arg
.order
, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX
);
830 spin_lock_init(&tq_arg
.lock
);
832 tq_arg
.name
= SPLAT_TASKQ_TEST6_NAME
;
834 for (i
= 0; i
< SPLAT_TASKQ_ORDER_MAX
; i
++) {
835 taskq_init_ent(&tqes
[i
]);
838 tq_id
[i
].arg
= &tq_arg
;
844 taskq_dispatch_ent(tq
, splat_taskq_test6_func
,
845 &tq_id
[i
], tflags
, &tqes
[i
]);
846 id
= tqes
[i
].tqent_id
;
848 id
= taskq_dispatch(tq
, splat_taskq_test6_func
,
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
));
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
);
867 /* Sleep to let tasks 1-3 start executing. */
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
);
879 splat_vprint(file
, SPLAT_TASKQ_TEST6_NAME
,
880 "Taskq '%s' destroying\n", tq_arg
.name
);
887 splat_taskq_test6(struct file
*file
, void *arg
)
891 rc
= splat_taskq_test6_impl(file
, arg
, B_FALSE
);
895 rc
= splat_taskq_test6_impl(file
, arg
, B_TRUE
);
901 splat_taskq_test7_func(void *arg
)
903 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)arg
;
908 if (tq_arg
->depth
>= SPLAT_TASKQ_DEPTH_MAX
)
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
),
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
;
924 id
= taskq_dispatch(tq_arg
->tq
, splat_taskq_test7_func
,
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
;
939 splat_taskq_test7_impl(struct file
*file
, void *arg
, boolean_t prealloc
)
943 splat_taskq_arg_t tq_arg
;
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
);
961 tq_arg
.name
= SPLAT_TASKQ_TEST7_NAME
;
965 taskq_init_ent(&tqe
);
971 splat_taskq_test7_func(&tq_arg
);
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
);
979 splat_vprint(file
, SPLAT_TASKQ_TEST7_NAME
,
980 "Taskq '%s' destroying\n", tq_arg
.name
);
983 return tq_arg
.depth
== SPLAT_TASKQ_DEPTH_MAX
? 0 : -EINVAL
;
987 splat_taskq_test7(struct file
*file
, void *arg
)
991 rc
= splat_taskq_test7_impl(file
, arg
, B_FALSE
);
995 rc
= splat_taskq_test7_impl(file
, arg
, B_TRUE
);
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.
1007 splat_taskq_test8_func(void *arg
)
1009 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)arg
;
1012 atomic_inc(&tq_arg
->count
);
1015 #define TEST8_NUM_TASKS 0x20000
1016 #define TEST8_THREADS_PER_TASKQ 100
1019 splat_taskq_test8_common(struct file
*file
, void *arg
, int minalloc
,
1024 splat_taskq_arg_t tq_arg
;
1028 tqes
= vmalloc(sizeof(*tqes
) * TEST8_NUM_TASKS
);
1031 memset(tqes
, 0, sizeof(*tqes
) * TEST8_NUM_TASKS
);
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
);
1048 tq_arg
.name
= SPLAT_TASKQ_TEST8_NAME
;
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
) {
1057 taskq_init_ent(tqes
[i
]);
1059 taskq_dispatch_ent(tq
, splat_taskq_test8_func
,
1060 &tq_arg
, TQ_SLEEP
, tqes
[i
]);
1062 id
= tqes
[i
]->tqent_id
;
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
);
1074 splat_vprint(file
, SPLAT_TASKQ_TEST8_NAME
, "Taskq '%s' "
1075 "waiting for %d dispatches\n", tq_arg
.name
,
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
);
1082 if (atomic_read(&tq_arg
.count
) != TEST8_NUM_TASKS
)
1086 splat_vprint(file
, SPLAT_TASKQ_TEST8_NAME
, "Taskq '%s' destroying\n",
1090 for (j
= 0; j
< TEST8_NUM_TASKS
&& tqes
[j
] != NULL
; j
++)
1098 splat_taskq_test8(struct file
*file
, void *arg
)
1102 rc
= splat_taskq_test8_common(file
, arg
, 1, 100);
1108 splat_taskq_init(void)
1110 splat_subsystem_t
*sub
;
1112 sub
= kmalloc(sizeof(*sub
), GFP_KERNEL
);
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
;
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
);
1145 splat_taskq_fini(splat_subsystem_t
*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
);
1161 splat_taskq_id(void) {
1162 return SPLAT_SUBSYSTEM_TASKQ
;