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 "splat-internal.h"
29 #define SPLAT_TASKQ_NAME "taskq"
30 #define SPLAT_TASKQ_DESC "Kernel Task Queue Tests"
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"
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"
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"
44 #define SPLAT_TASKQ_TEST4_ID 0x0204
45 #define SPLAT_TASKQ_TEST4_NAME "wait"
46 #define SPLAT_TASKQ_TEST4_DESC "Multiple task waiting"
48 #define SPLAT_TASKQ_TEST5_ID 0x0205
49 #define SPLAT_TASKQ_TEST5_NAME "order"
50 #define SPLAT_TASKQ_TEST5_DESC "Correct task ordering"
52 #define SPLAT_TASKQ_ORDER_MAX 8
54 typedef struct splat_taskq_arg
{
58 int order
[SPLAT_TASKQ_ORDER_MAX
];
64 typedef struct splat_taskq_id
{
66 splat_taskq_arg_t
*arg
;
70 * Create a taskq, queue a task, wait until task completes, ensure
71 * task ran properly, cleanup taskq.
74 splat_taskq_test13_func(void *arg
)
76 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)arg
;
79 splat_vprint(tq_arg
->file
, SPLAT_TASKQ_TEST1_NAME
,
80 "Taskq '%s' function '%s' setting flag\n",
81 tq_arg
->name
, sym2str(splat_taskq_test13_func
));
86 splat_taskq_test1(struct file
*file
, void *arg
)
90 splat_taskq_arg_t tq_arg
;
92 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
, "Taskq '%s' creating\n",
93 SPLAT_TASKQ_TEST1_NAME
);
94 if ((tq
= taskq_create(SPLAT_TASKQ_TEST1_NAME
, 1, maxclsyspri
,
95 50, INT_MAX
, TASKQ_PREPOPULATE
)) == NULL
) {
96 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
,
97 "Taskq '%s' create failed\n",
98 SPLAT_TASKQ_TEST1_NAME
);
105 tq_arg
.name
= SPLAT_TASKQ_TEST1_NAME
;
107 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
,
108 "Taskq '%s' function '%s' dispatching\n",
109 tq_arg
.name
, sym2str(splat_taskq_test13_func
));
110 if ((id
= taskq_dispatch(tq
, splat_taskq_test13_func
,
111 &tq_arg
, TQ_SLEEP
)) == 0) {
112 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
,
113 "Taskq '%s' function '%s' dispatch failed\n",
114 tq_arg
.name
, sym2str(splat_taskq_test13_func
));
119 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
, "Taskq '%s' waiting\n",
122 splat_vprint(file
, SPLAT_TASKQ_TEST1_NAME
, "Taskq '%s' destroying\n",
126 return (tq_arg
.flag
) ? 0 : -EINVAL
;
130 * Create multiple taskq's, each with multiple tasks, wait until
131 * all tasks complete, ensure all tasks ran properly and in the
132 * correct order. Run order must be the same as the order submitted
133 * because we only have 1 thread per taskq. Finally cleanup the taskq.
136 splat_taskq_test2_func1(void *arg
)
138 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)arg
;
141 splat_vprint(tq_arg
->file
, SPLAT_TASKQ_TEST2_NAME
,
142 "Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
143 tq_arg
->name
, tq_arg
->id
,
144 sym2str(splat_taskq_test2_func1
),
145 tq_arg
->flag
* 2, tq_arg
->flag
);
150 splat_taskq_test2_func2(void *arg
)
152 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)arg
;
155 splat_vprint(tq_arg
->file
, SPLAT_TASKQ_TEST2_NAME
,
156 "Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
157 tq_arg
->name
, tq_arg
->id
,
158 sym2str(splat_taskq_test2_func2
),
159 tq_arg
->flag
+ 1, tq_arg
->flag
);
163 #define TEST2_TASKQS 8
164 #define TEST2_THREADS_PER_TASKQ 1
167 splat_taskq_test2(struct file
*file
, void *arg
) {
168 taskq_t
*tq
[TEST2_TASKQS
] = { NULL
};
170 splat_taskq_arg_t tq_args
[TEST2_TASKQS
];
173 for (i
= 0; i
< TEST2_TASKQS
; i
++) {
175 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
, "Taskq '%s/%d' "
176 "creating\n", SPLAT_TASKQ_TEST2_NAME
, i
);
177 if ((tq
[i
] = taskq_create(SPLAT_TASKQ_TEST2_NAME
,
178 TEST2_THREADS_PER_TASKQ
,
179 maxclsyspri
, 50, INT_MAX
,
180 TASKQ_PREPOPULATE
)) == NULL
) {
181 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
182 "Taskq '%s/%d' create failed\n",
183 SPLAT_TASKQ_TEST2_NAME
, i
);
190 tq_args
[i
].file
= file
;
191 tq_args
[i
].name
= SPLAT_TASKQ_TEST2_NAME
;
193 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
194 "Taskq '%s/%d' function '%s' dispatching\n",
195 tq_args
[i
].name
, tq_args
[i
].id
,
196 sym2str(splat_taskq_test2_func1
));
197 if ((id
= taskq_dispatch(
198 tq
[i
], splat_taskq_test2_func1
,
199 &tq_args
[i
], TQ_SLEEP
)) == 0) {
200 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
201 "Taskq '%s/%d' function '%s' dispatch "
202 "failed\n", tq_args
[i
].name
, tq_args
[i
].id
,
203 sym2str(splat_taskq_test2_func1
));
208 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
209 "Taskq '%s/%d' function '%s' dispatching\n",
210 tq_args
[i
].name
, tq_args
[i
].id
,
211 sym2str(splat_taskq_test2_func2
));
212 if ((id
= taskq_dispatch(
213 tq
[i
], splat_taskq_test2_func2
,
214 &tq_args
[i
], TQ_SLEEP
)) == 0) {
215 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
216 "Taskq '%s/%d' function '%s' dispatch failed\n",
217 tq_args
[i
].name
, tq_args
[i
].id
,
218 sym2str(splat_taskq_test2_func2
));
224 /* When rc is set we're effectively just doing cleanup here, so
225 * ignore new errors in that case. They just cause noise. */
226 for (i
= 0; i
< TEST2_TASKQS
; i
++) {
228 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
229 "Taskq '%s/%d' waiting\n",
230 tq_args
[i
].name
, tq_args
[i
].id
);
232 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
233 "Taskq '%s/%d; destroying\n",
234 tq_args
[i
].name
, tq_args
[i
].id
);
235 taskq_destroy(tq
[i
]);
237 if (!rc
&& tq_args
[i
].flag
!= ((i
* 2) + 1)) {
238 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
239 "Taskq '%s/%d' processed tasks "
240 "out of order; %d != %d\n",
241 tq_args
[i
].name
, tq_args
[i
].id
,
242 tq_args
[i
].flag
, i
* 2 + 1);
245 splat_vprint(file
, SPLAT_TASKQ_TEST2_NAME
,
246 "Taskq '%s/%d' processed tasks "
247 "in the correct order; %d == %d\n",
248 tq_args
[i
].name
, tq_args
[i
].id
,
249 tq_args
[i
].flag
, i
* 2 + 1);
258 * Use the global system task queue with a single task, wait until task
259 * completes, ensure task ran properly.
262 splat_taskq_test3(struct file
*file
, void *arg
)
265 splat_taskq_arg_t tq_arg
;
270 tq_arg
.name
= SPLAT_TASKQ_TEST3_NAME
;
272 splat_vprint(file
, SPLAT_TASKQ_TEST3_NAME
,
273 "Taskq '%s' function '%s' dispatching\n",
274 tq_arg
.name
, sym2str(splat_taskq_test13_func
));
275 if ((id
= taskq_dispatch(system_taskq
, splat_taskq_test13_func
,
276 &tq_arg
, TQ_SLEEP
)) == 0) {
277 splat_vprint(file
, SPLAT_TASKQ_TEST3_NAME
,
278 "Taskq '%s' function '%s' dispatch failed\n",
279 tq_arg
.name
, sym2str(splat_taskq_test13_func
));
283 splat_vprint(file
, SPLAT_TASKQ_TEST3_NAME
, "Taskq '%s' waiting\n",
285 taskq_wait(system_taskq
);
287 return (tq_arg
.flag
) ? 0 : -EINVAL
;
291 * Create a taskq and dispatch a large number of tasks to the queue.
292 * Then use taskq_wait() to block until all the tasks complete, then
293 * cross check that all the tasks ran by checking tg_arg->count which
294 * is incremented in the task function. Finally cleanup the taskq.
297 splat_taskq_test4_func(void *arg
)
299 splat_taskq_arg_t
*tq_arg
= (splat_taskq_arg_t
*)arg
;
302 atomic_inc(&tq_arg
->count
);
306 splat_taskq_test4(struct file
*file
, void *arg
)
309 splat_taskq_arg_t tq_arg
;
312 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
, "Taskq '%s' creating\n",
313 SPLAT_TASKQ_TEST4_NAME
);
314 if ((tq
= taskq_create(SPLAT_TASKQ_TEST4_NAME
, 1, maxclsyspri
,
315 50, INT_MAX
, TASKQ_PREPOPULATE
)) == NULL
) {
316 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
,
317 "Taskq '%s' create failed\n",
318 SPLAT_TASKQ_TEST4_NAME
);
323 tq_arg
.name
= SPLAT_TASKQ_TEST4_NAME
;
325 for (i
= 1; i
<= 1024; i
*= 2) {
326 atomic_set(&tq_arg
.count
, 0);
327 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
,
328 "Taskq '%s' function '%s' dispatched %d times\n",
329 tq_arg
.name
, sym2str(splat_taskq_test4_func
), i
);
331 for (j
= 0; j
< i
; j
++) {
332 if ((taskq_dispatch(tq
, splat_taskq_test4_func
,
333 &tq_arg
, TQ_SLEEP
)) == 0) {
334 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
,
335 "Taskq '%s' function '%s' dispatch "
336 "%d failed\n", tq_arg
.name
,
337 sym2str(splat_taskq_test13_func
), j
);
343 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
, "Taskq '%s' "
344 "waiting for %d dispatches\n", tq_arg
.name
, i
);
346 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
, "Taskq '%s' "
347 "%d/%d dispatches finished\n", tq_arg
.name
,
348 atomic_read(&tq_arg
.count
), i
);
349 if (atomic_read(&tq_arg
.count
) != i
) {
356 splat_vprint(file
, SPLAT_TASKQ_TEST4_NAME
, "Taskq '%s' destroying\n",
364 * Create a taskq and dispatch a specific sequence of tasks carefully
365 * crafted to validate the order in which tasks are processed. When
366 * there are multiple worker threads each thread will process the
367 * next pending task as soon as it completes its current task. This
368 * means that tasks do not strictly complete in order in which they
369 * were dispatched (increasing task id). This is fine but we need to
370 * verify that taskq_wait_id() blocks until the passed task id and all
371 * lower task ids complete. We do this by dispatching the following
372 * specific sequence of tasks each of which block for N time units.
373 * We then use taskq_wait_id() to unblock at specific task id and
374 * verify the only the expected task ids have completed and in the
375 * correct order. The two cases of interest are:
377 * 1) Task ids larger than the waited for task id can run and
378 * complete as long as there is an available worker thread.
379 * 2) All task ids lower than the waited one must complete before
380 * unblocking even if the waited task id itself has completed.
382 * The following table shows each task id and how they will be
383 * scheduled. Each rows represent one time unit and each column
384 * one of the three worker threads. The places taskq_wait_id()
385 * must unblock for a specific id are identified as well as the
386 * task ids which must have completed and their order.
388 * +-----+ <--- taskq_wait_id(tq, 8) unblocks
389 * | | Required Completion Order: 1,2,4,5,3
394 * | | +-----+ <--- taskq_wait_id(tq, 3) unblocks
395 * | | 7 | | Required Completion Order: 1,2,4,5,3,8,6,7
404 * +-----+-----+-----+
408 splat_taskq_test5_func(void *arg
)
410 splat_taskq_id_t
*tq_id
= (splat_taskq_id_t
*)arg
;
411 splat_taskq_arg_t
*tq_arg
= tq_id
->arg
;
414 /* Delays determined by above table */
416 default: factor
= 0; break;
417 case 1: case 8: factor
= 1; break;
418 case 2: case 4: case 5: factor
= 2; break;
419 case 6: case 7: factor
= 4; break;
420 case 3: factor
= 5; break;
423 msleep(factor
* 100);
424 splat_vprint(tq_arg
->file
, tq_arg
->name
,
425 "Taskqid %d complete for taskq '%s'\n",
426 tq_id
->id
, tq_arg
->name
);
428 spin_lock(&tq_arg
->lock
);
429 tq_arg
->order
[tq_arg
->flag
] = tq_id
->id
;
431 spin_unlock(&tq_arg
->lock
);
435 splat_taskq_test5_order(splat_taskq_arg_t
*tq_arg
, int *order
)
439 for (i
= 0; i
< SPLAT_TASKQ_ORDER_MAX
; i
++) {
440 if (tq_arg
->order
[i
] != order
[i
]) {
441 splat_vprint(tq_arg
->file
, SPLAT_TASKQ_TEST5_NAME
,
442 "Taskq '%s' incorrect completion "
443 "order\n", tq_arg
->name
);
444 splat_vprint(tq_arg
->file
, SPLAT_TASKQ_TEST5_NAME
,
445 "%s", "Expected { ");
447 for (j
= 0; j
< SPLAT_TASKQ_ORDER_MAX
; j
++)
448 splat_print(tq_arg
->file
, "%d ", order
[j
]);
450 splat_print(tq_arg
->file
, "%s", "}\n");
451 splat_vprint(tq_arg
->file
, SPLAT_TASKQ_TEST5_NAME
,
454 for (j
= 0; j
< SPLAT_TASKQ_ORDER_MAX
; j
++)
455 splat_print(tq_arg
->file
, "%d ",
458 splat_print(tq_arg
->file
, "%s", "}\n");
463 splat_vprint(tq_arg
->file
, SPLAT_TASKQ_TEST5_NAME
,
464 "Taskq '%s' validated correct completion order\n",
471 splat_taskq_test5(struct file
*file
, void *arg
)
475 splat_taskq_id_t tq_id
[SPLAT_TASKQ_ORDER_MAX
];
476 splat_taskq_arg_t tq_arg
;
477 int order1
[SPLAT_TASKQ_ORDER_MAX
] = { 1,2,4,5,3,0,0,0 };
478 int order2
[SPLAT_TASKQ_ORDER_MAX
] = { 1,2,4,5,3,8,6,7 };
481 splat_vprint(file
, SPLAT_TASKQ_TEST5_NAME
, "Taskq '%s' creating\n",
482 SPLAT_TASKQ_TEST5_NAME
);
483 if ((tq
= taskq_create(SPLAT_TASKQ_TEST5_NAME
, 3, maxclsyspri
,
484 50, INT_MAX
, TASKQ_PREPOPULATE
)) == NULL
) {
485 splat_vprint(file
, SPLAT_TASKQ_TEST5_NAME
,
486 "Taskq '%s' create failed\n",
487 SPLAT_TASKQ_TEST5_NAME
);
492 memset(&tq_arg
.order
, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX
);
493 spin_lock_init(&tq_arg
.lock
);
495 tq_arg
.name
= SPLAT_TASKQ_TEST5_NAME
;
497 for (i
= 0; i
< SPLAT_TASKQ_ORDER_MAX
; i
++) {
499 tq_id
[i
].arg
= &tq_arg
;
501 if ((id
= taskq_dispatch(tq
, splat_taskq_test5_func
,
502 &tq_id
[i
], TQ_SLEEP
)) == 0) {
503 splat_vprint(file
, SPLAT_TASKQ_TEST5_NAME
,
504 "Taskq '%s' function '%s' dispatch failed\n",
505 tq_arg
.name
, sym2str(splat_taskq_test5_func
));
510 if (tq_id
[i
].id
!= id
) {
511 splat_vprint(file
, SPLAT_TASKQ_TEST5_NAME
,
512 "Taskq '%s' expected taskqid %d got %d\n",
513 tq_arg
.name
, (int)tq_id
[i
].id
, (int)id
);
519 splat_vprint(file
, SPLAT_TASKQ_TEST5_NAME
, "Taskq '%s' "
520 "waiting for taskqid %d completion\n", tq_arg
.name
, 3);
521 taskq_wait_id(tq
, 3);
522 if ((rc
= splat_taskq_test5_order(&tq_arg
, order1
)))
525 splat_vprint(file
, SPLAT_TASKQ_TEST5_NAME
, "Taskq '%s' "
526 "waiting for taskqid %d completion\n", tq_arg
.name
, 8);
527 taskq_wait_id(tq
, 8);
528 rc
= splat_taskq_test5_order(&tq_arg
, order2
);
531 splat_vprint(file
, SPLAT_TASKQ_TEST5_NAME
,
532 "Taskq '%s' destroying\n", tq_arg
.name
);
539 splat_taskq_init(void)
541 splat_subsystem_t
*sub
;
543 sub
= kmalloc(sizeof(*sub
), GFP_KERNEL
);
547 memset(sub
, 0, sizeof(*sub
));
548 strncpy(sub
->desc
.name
, SPLAT_TASKQ_NAME
, SPLAT_NAME_SIZE
);
549 strncpy(sub
->desc
.desc
, SPLAT_TASKQ_DESC
, SPLAT_DESC_SIZE
);
550 INIT_LIST_HEAD(&sub
->subsystem_list
);
551 INIT_LIST_HEAD(&sub
->test_list
);
552 spin_lock_init(&sub
->test_lock
);
553 sub
->desc
.id
= SPLAT_SUBSYSTEM_TASKQ
;
555 SPLAT_TEST_INIT(sub
, SPLAT_TASKQ_TEST1_NAME
, SPLAT_TASKQ_TEST1_DESC
,
556 SPLAT_TASKQ_TEST1_ID
, splat_taskq_test1
);
557 SPLAT_TEST_INIT(sub
, SPLAT_TASKQ_TEST2_NAME
, SPLAT_TASKQ_TEST2_DESC
,
558 SPLAT_TASKQ_TEST2_ID
, splat_taskq_test2
);
559 SPLAT_TEST_INIT(sub
, SPLAT_TASKQ_TEST3_NAME
, SPLAT_TASKQ_TEST3_DESC
,
560 SPLAT_TASKQ_TEST3_ID
, splat_taskq_test3
);
561 SPLAT_TEST_INIT(sub
, SPLAT_TASKQ_TEST4_NAME
, SPLAT_TASKQ_TEST4_DESC
,
562 SPLAT_TASKQ_TEST4_ID
, splat_taskq_test4
);
563 SPLAT_TEST_INIT(sub
, SPLAT_TASKQ_TEST5_NAME
, SPLAT_TASKQ_TEST5_DESC
,
564 SPLAT_TASKQ_TEST5_ID
, splat_taskq_test5
);
570 splat_taskq_fini(splat_subsystem_t
*sub
)
573 SPLAT_TEST_FINI(sub
, SPLAT_TASKQ_TEST5_ID
);
574 SPLAT_TEST_FINI(sub
, SPLAT_TASKQ_TEST4_ID
);
575 SPLAT_TEST_FINI(sub
, SPLAT_TASKQ_TEST3_ID
);
576 SPLAT_TEST_FINI(sub
, SPLAT_TASKQ_TEST2_ID
);
577 SPLAT_TEST_FINI(sub
, SPLAT_TASKQ_TEST1_ID
);
583 splat_taskq_id(void) {
584 return SPLAT_SUBSYSTEM_TASKQ
;