]> git.proxmox.com Git - mirror_spl.git/blame - module/splat/splat-taskq.c
Add TASKQID_INVALID and TASKQID_INITIAL macros
[mirror_spl.git] / module / splat / splat-taskq.c
CommitLineData
716154c5
BB
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>.
715f6251 6 * UCRL-CODE-235197
7 *
716154c5 8 * This file is part of the SPL, Solaris Porting Layer.
3d6af2dd 9 * For details, see <http://zfsonlinux.org/>.
716154c5
BB
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.
715f6251 15 *
716154c5 16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
715f6251 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
716154c5
BB
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting LAyer Tests (SPLAT) Task Queue Tests.
25\*****************************************************************************/
715f6251 26
df870a69 27#include <sys/kmem.h>
e5b9b344 28#include <sys/vmem.h>
52479ecf
NB
29#include <sys/random.h>
30#include <sys/taskq.h>
f7a973d9 31#include <sys/time.h>
e5b9b344 32#include <sys/timer.h>
52479ecf 33#include <linux/delay.h>
7c50328b 34#include "splat-internal.h"
f1ca4da6 35
7c50328b 36#define SPLAT_TASKQ_NAME "taskq"
37#define SPLAT_TASKQ_DESC "Kernel Task Queue Tests"
f1ca4da6 38
7c50328b 39#define SPLAT_TASKQ_TEST1_ID 0x0201
40#define SPLAT_TASKQ_TEST1_NAME "single"
41#define SPLAT_TASKQ_TEST1_DESC "Single task queue, single task"
f1ca4da6 42
5562e5d1 43#define SPLAT_TASKQ_TEST2_ID 0x0202
7c50328b 44#define SPLAT_TASKQ_TEST2_NAME "multiple"
45#define SPLAT_TASKQ_TEST2_DESC "Multiple task queues, multiple tasks"
f1ca4da6 46
5562e5d1 47#define SPLAT_TASKQ_TEST3_ID 0x0203
e9cb2b4f
BB
48#define SPLAT_TASKQ_TEST3_NAME "system"
49#define SPLAT_TASKQ_TEST3_DESC "System task queue, multiple tasks"
50
5562e5d1 51#define SPLAT_TASKQ_TEST4_ID 0x0204
7257ec41
BB
52#define SPLAT_TASKQ_TEST4_NAME "wait"
53#define SPLAT_TASKQ_TEST4_DESC "Multiple task waiting"
54
5562e5d1
BB
55#define SPLAT_TASKQ_TEST5_ID 0x0205
56#define SPLAT_TASKQ_TEST5_NAME "order"
57#define SPLAT_TASKQ_TEST5_DESC "Correct task ordering"
58
55f10ae5
NB
59#define SPLAT_TASKQ_TEST6_ID 0x0206
60#define SPLAT_TASKQ_TEST6_NAME "front"
61#define SPLAT_TASKQ_TEST6_DESC "Correct ordering with TQ_FRONT flag"
62
ac1e5b60
PS
63#define SPLAT_TASKQ_TEST7_ID 0x0207
64#define SPLAT_TASKQ_TEST7_NAME "recurse"
65#define SPLAT_TASKQ_TEST7_DESC "Single task queue, recursive dispatch"
66
cf5d23fa
NB
67#define SPLAT_TASKQ_TEST8_ID 0x0208
68#define SPLAT_TASKQ_TEST8_NAME "contention"
69#define SPLAT_TASKQ_TEST8_DESC "1 queue, 100 threads, 131072 tasks"
70
2f357826
BB
71#define SPLAT_TASKQ_TEST9_ID 0x0209
72#define SPLAT_TASKQ_TEST9_NAME "delay"
73#define SPLAT_TASKQ_TEST9_DESC "Delayed task execution"
74
3238e717
BB
75#define SPLAT_TASKQ_TEST10_ID 0x020a
76#define SPLAT_TASKQ_TEST10_NAME "cancel"
77#define SPLAT_TASKQ_TEST10_DESC "Cancel task execution"
78
f7a973d9
BB
79#define SPLAT_TASKQ_TEST11_ID 0x020b
80#define SPLAT_TASKQ_TEST11_NAME "dynamic"
81#define SPLAT_TASKQ_TEST11_DESC "Dynamic task queue thread creation"
82
5562e5d1 83#define SPLAT_TASKQ_ORDER_MAX 8
ac1e5b60 84#define SPLAT_TASKQ_DEPTH_MAX 16
5562e5d1 85
cf5d23fa 86
7c50328b 87typedef struct splat_taskq_arg {
f1ca4da6 88 int flag;
89 int id;
2f357826 90 atomic_t *count;
5562e5d1 91 int order[SPLAT_TASKQ_ORDER_MAX];
ac1e5b60 92 unsigned int depth;
545e9ac0 93 clock_t expire;
ac1e5b60 94 taskq_t *tq;
699d5ee8 95 taskq_ent_t *tqe;
5562e5d1 96 spinlock_t lock;
f1ca4da6 97 struct file *file;
98 const char *name;
7c50328b 99} splat_taskq_arg_t;
f1ca4da6 100
5562e5d1
BB
101typedef struct splat_taskq_id {
102 int id;
103 splat_taskq_arg_t *arg;
104} splat_taskq_id_t;
105
106/*
107 * Create a taskq, queue a task, wait until task completes, ensure
108 * task ran properly, cleanup taskq.
f1ca4da6 109 */
110static void
e9cb2b4f 111splat_taskq_test13_func(void *arg)
f1ca4da6 112{
7c50328b 113 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
f1ca4da6 114
115 ASSERT(tq_arg);
7c50328b 116 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST1_NAME,
f1ca4da6 117 "Taskq '%s' function '%s' setting flag\n",
e9cb2b4f 118 tq_arg->name, sym2str(splat_taskq_test13_func));
f1ca4da6 119 tq_arg->flag = 1;
120}
121
122static int
699d5ee8 123splat_taskq_test1_impl(struct file *file, void *arg, boolean_t prealloc)
f1ca4da6 124{
125 taskq_t *tq;
126 taskqid_t id;
7c50328b 127 splat_taskq_arg_t tq_arg;
03a78353 128 taskq_ent_t *tqe;
699d5ee8 129
03a78353
BB
130 tqe = kmem_alloc(sizeof (taskq_ent_t), KM_SLEEP);
131 taskq_init_ent(tqe);
f1ca4da6 132
699d5ee8
PS
133 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
134 "Taskq '%s' creating (%s dispatch)\n",
135 SPLAT_TASKQ_TEST1_NAME,
136 prealloc ? "prealloc" : "dynamic");
62aa81a5 137 if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, defclsyspri,
bcd68186 138 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
7c50328b 139 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
f1ca4da6 140 "Taskq '%s' create failed\n",
7c50328b 141 SPLAT_TASKQ_TEST1_NAME);
03a78353 142 kmem_free(tqe, sizeof (taskq_ent_t));
f1ca4da6 143 return -EINVAL;
144 }
145
146 tq_arg.flag = 0;
147 tq_arg.id = 0;
148 tq_arg.file = file;
7c50328b 149 tq_arg.name = SPLAT_TASKQ_TEST1_NAME;
f1ca4da6 150
7c50328b 151 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
f1ca4da6 152 "Taskq '%s' function '%s' dispatching\n",
e9cb2b4f 153 tq_arg.name, sym2str(splat_taskq_test13_func));
699d5ee8
PS
154 if (prealloc) {
155 taskq_dispatch_ent(tq, splat_taskq_test13_func,
03a78353
BB
156 &tq_arg, TQ_SLEEP, tqe);
157 id = tqe->tqent_id;
699d5ee8
PS
158 } else {
159 id = taskq_dispatch(tq, splat_taskq_test13_func,
160 &tq_arg, TQ_SLEEP);
161 }
162
cbba7146 163 if (id == TASKQID_INVALID) {
7c50328b 164 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
699d5ee8
PS
165 "Taskq '%s' function '%s' dispatch failed\n",
166 tq_arg.name, sym2str(splat_taskq_test13_func));
03a78353 167 kmem_free(tqe, sizeof (taskq_ent_t));
4098c921 168 taskq_destroy(tq);
f1ca4da6 169 return -EINVAL;
170 }
171
7c50328b 172 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
f1ca4da6 173 tq_arg.name);
174 taskq_wait(tq);
7c50328b 175 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
f1ca4da6 176 tq_arg.name);
699d5ee8 177
03a78353 178 kmem_free(tqe, sizeof (taskq_ent_t));
4098c921 179 taskq_destroy(tq);
f1ca4da6 180
181 return (tq_arg.flag) ? 0 : -EINVAL;
182}
183
699d5ee8
PS
184static int
185splat_taskq_test1(struct file *file, void *arg)
186{
187 int rc;
188
189 rc = splat_taskq_test1_impl(file, arg, B_FALSE);
190 if (rc)
191 return rc;
192
193 rc = splat_taskq_test1_impl(file, arg, B_TRUE);
194
195 return rc;
196}
197
5562e5d1
BB
198/*
199 * Create multiple taskq's, each with multiple tasks, wait until
200 * all tasks complete, ensure all tasks ran properly and in the
201 * correct order. Run order must be the same as the order submitted
202 * because we only have 1 thread per taskq. Finally cleanup the taskq.
f1ca4da6 203 */
204static void
7c50328b 205splat_taskq_test2_func1(void *arg)
f1ca4da6 206{
7c50328b 207 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
f1ca4da6 208
209 ASSERT(tq_arg);
7c50328b 210 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 211 "Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
212 tq_arg->name, tq_arg->id,
7c50328b 213 sym2str(splat_taskq_test2_func1),
f1ca4da6 214 tq_arg->flag * 2, tq_arg->flag);
215 tq_arg->flag *= 2;
216}
217
218static void
7c50328b 219splat_taskq_test2_func2(void *arg)
f1ca4da6 220{
7c50328b 221 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
f1ca4da6 222
223 ASSERT(tq_arg);
7c50328b 224 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 225 "Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
226 tq_arg->name, tq_arg->id,
7c50328b 227 sym2str(splat_taskq_test2_func2),
f1ca4da6 228 tq_arg->flag + 1, tq_arg->flag);
229 tq_arg->flag += 1;
230}
231
232#define TEST2_TASKQS 8
5562e5d1 233#define TEST2_THREADS_PER_TASKQ 1
bcd68186 234
f1ca4da6 235static int
699d5ee8 236splat_taskq_test2_impl(struct file *file, void *arg, boolean_t prealloc) {
f1ca4da6 237 taskq_t *tq[TEST2_TASKQS] = { NULL };
238 taskqid_t id;
6ab08667 239 splat_taskq_arg_t *tq_args[TEST2_TASKQS] = { NULL };
699d5ee8
PS
240 taskq_ent_t *func1_tqes = NULL;
241 taskq_ent_t *func2_tqes = NULL;
f1ca4da6 242 int i, rc = 0;
243
699d5ee8
PS
244 func1_tqes = kmalloc(sizeof(*func1_tqes) * TEST2_TASKQS, GFP_KERNEL);
245 if (func1_tqes == NULL) {
246 rc = -ENOMEM;
247 goto out;
248 }
249
250 func2_tqes = kmalloc(sizeof(*func2_tqes) * TEST2_TASKQS, GFP_KERNEL);
251 if (func2_tqes == NULL) {
252 rc = -ENOMEM;
253 goto out;
254 }
255
f1ca4da6 256 for (i = 0; i < TEST2_TASKQS; i++) {
699d5ee8
PS
257 taskq_init_ent(&func1_tqes[i]);
258 taskq_init_ent(&func2_tqes[i]);
f1ca4da6 259
6ab08667
BB
260 tq_args[i] = kmalloc(sizeof (splat_taskq_arg_t), GFP_KERNEL);
261 if (tq_args[i] == NULL) {
262 rc = -ENOMEM;
263 break;
264 }
265
699d5ee8
PS
266 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
267 "Taskq '%s/%d' creating (%s dispatch)\n",
268 SPLAT_TASKQ_TEST2_NAME, i,
269 prealloc ? "prealloc" : "dynamic");
7c50328b 270 if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME,
bcd68186 271 TEST2_THREADS_PER_TASKQ,
62aa81a5 272 defclsyspri, 50, INT_MAX,
bcd68186 273 TASKQ_PREPOPULATE)) == NULL) {
7c50328b 274 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 275 "Taskq '%s/%d' create failed\n",
7c50328b 276 SPLAT_TASKQ_TEST2_NAME, i);
f1ca4da6 277 rc = -EINVAL;
278 break;
279 }
280
6ab08667
BB
281 tq_args[i]->flag = i;
282 tq_args[i]->id = i;
283 tq_args[i]->file = file;
284 tq_args[i]->name = SPLAT_TASKQ_TEST2_NAME;
f1ca4da6 285
7c50328b 286 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 287 "Taskq '%s/%d' function '%s' dispatching\n",
6ab08667 288 tq_args[i]->name, tq_args[i]->id,
7c50328b 289 sym2str(splat_taskq_test2_func1));
699d5ee8
PS
290 if (prealloc) {
291 taskq_dispatch_ent(tq[i], splat_taskq_test2_func1,
6ab08667 292 tq_args[i], TQ_SLEEP, &func1_tqes[i]);
699d5ee8
PS
293 id = func1_tqes[i].tqent_id;
294 } else {
295 id = taskq_dispatch(tq[i], splat_taskq_test2_func1,
6ab08667 296 tq_args[i], TQ_SLEEP);
699d5ee8
PS
297 }
298
cbba7146 299 if (id == TASKQID_INVALID) {
7c50328b 300 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 301 "Taskq '%s/%d' function '%s' dispatch "
6ab08667 302 "failed\n", tq_args[i]->name, tq_args[i]->id,
7c50328b 303 sym2str(splat_taskq_test2_func1));
f1ca4da6 304 rc = -EINVAL;
305 break;
306 }
307
7c50328b 308 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 309 "Taskq '%s/%d' function '%s' dispatching\n",
6ab08667 310 tq_args[i]->name, tq_args[i]->id,
7c50328b 311 sym2str(splat_taskq_test2_func2));
699d5ee8
PS
312 if (prealloc) {
313 taskq_dispatch_ent(tq[i], splat_taskq_test2_func2,
6ab08667 314 tq_args[i], TQ_SLEEP, &func2_tqes[i]);
699d5ee8
PS
315 id = func2_tqes[i].tqent_id;
316 } else {
317 id = taskq_dispatch(tq[i], splat_taskq_test2_func2,
6ab08667 318 tq_args[i], TQ_SLEEP);
699d5ee8
PS
319 }
320
cbba7146 321 if (id == TASKQID_INVALID) {
699d5ee8
PS
322 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq "
323 "'%s/%d' function '%s' dispatch failed\n",
6ab08667 324 tq_args[i]->name, tq_args[i]->id,
699d5ee8 325 sym2str(splat_taskq_test2_func2));
f1ca4da6 326 rc = -EINVAL;
327 break;
328 }
329 }
330
331 /* When rc is set we're effectively just doing cleanup here, so
332 * ignore new errors in that case. They just cause noise. */
333 for (i = 0; i < TEST2_TASKQS; i++) {
6ab08667
BB
334 if (tq_args[i] == NULL)
335 continue;
336
f1ca4da6 337 if (tq[i] != NULL) {
7c50328b 338 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 339 "Taskq '%s/%d' waiting\n",
6ab08667 340 tq_args[i]->name, tq_args[i]->id);
f1ca4da6 341 taskq_wait(tq[i]);
7c50328b 342 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 343 "Taskq '%s/%d; destroying\n",
6ab08667 344 tq_args[i]->name, tq_args[i]->id);
699d5ee8 345
4098c921 346 taskq_destroy(tq[i]);
f1ca4da6 347
6ab08667 348 if (!rc && tq_args[i]->flag != ((i * 2) + 1)) {
7c50328b 349 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 350 "Taskq '%s/%d' processed tasks "
351 "out of order; %d != %d\n",
6ab08667
BB
352 tq_args[i]->name, tq_args[i]->id,
353 tq_args[i]->flag, i * 2 + 1);
f1ca4da6 354 rc = -EINVAL;
355 } else {
7c50328b 356 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
f1ca4da6 357 "Taskq '%s/%d' processed tasks "
358 "in the correct order; %d == %d\n",
6ab08667
BB
359 tq_args[i]->name, tq_args[i]->id,
360 tq_args[i]->flag, i * 2 + 1);
f1ca4da6 361 }
6ab08667
BB
362
363 kfree(tq_args[i]);
f1ca4da6 364 }
365 }
699d5ee8
PS
366out:
367 if (func1_tqes)
368 kfree(func1_tqes);
369
370 if (func2_tqes)
371 kfree(func2_tqes);
372
373 return rc;
374}
375
376static int
377splat_taskq_test2(struct file *file, void *arg) {
378 int rc;
379
380 rc = splat_taskq_test2_impl(file, arg, B_FALSE);
381 if (rc)
382 return rc;
383
384 rc = splat_taskq_test2_impl(file, arg, B_TRUE);
f1ca4da6 385
386 return rc;
387}
388
5562e5d1
BB
389/*
390 * Use the global system task queue with a single task, wait until task
391 * completes, ensure task ran properly.
e9cb2b4f
BB
392 */
393static int
699d5ee8 394splat_taskq_test3_impl(struct file *file, void *arg, boolean_t prealloc)
e9cb2b4f
BB
395{
396 taskqid_t id;
03a78353
BB
397 splat_taskq_arg_t *tq_arg;
398 taskq_ent_t *tqe;
399 int error;
699d5ee8 400
03a78353
BB
401 tq_arg = kmem_alloc(sizeof (splat_taskq_arg_t), KM_SLEEP);
402 tqe = kmem_alloc(sizeof (taskq_ent_t), KM_SLEEP);
403 taskq_init_ent(tqe);
e9cb2b4f 404
03a78353
BB
405 tq_arg->flag = 0;
406 tq_arg->id = 0;
407 tq_arg->file = file;
408 tq_arg->name = SPLAT_TASKQ_TEST3_NAME;
e9cb2b4f
BB
409
410 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
699d5ee8 411 "Taskq '%s' function '%s' %s dispatch\n",
03a78353 412 tq_arg->name, sym2str(splat_taskq_test13_func),
699d5ee8
PS
413 prealloc ? "prealloc" : "dynamic");
414 if (prealloc) {
415 taskq_dispatch_ent(system_taskq, splat_taskq_test13_func,
03a78353
BB
416 tq_arg, TQ_SLEEP, tqe);
417 id = tqe->tqent_id;
699d5ee8
PS
418 } else {
419 id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
03a78353 420 tq_arg, TQ_SLEEP);
699d5ee8
PS
421 }
422
cbba7146 423 if (id == TASKQID_INVALID) {
e9cb2b4f
BB
424 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
425 "Taskq '%s' function '%s' dispatch failed\n",
03a78353
BB
426 tq_arg->name, sym2str(splat_taskq_test13_func));
427 kmem_free(tqe, sizeof (taskq_ent_t));
428 kmem_free(tq_arg, sizeof (splat_taskq_arg_t));
e9cb2b4f
BB
429 return -EINVAL;
430 }
431
432 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
03a78353 433 tq_arg->name);
e9cb2b4f
BB
434 taskq_wait(system_taskq);
435
03a78353
BB
436 error = (tq_arg->flag) ? 0 : -EINVAL;
437
438 kmem_free(tqe, sizeof (taskq_ent_t));
439 kmem_free(tq_arg, sizeof (splat_taskq_arg_t));
440
441 return (error);
e9cb2b4f
BB
442}
443
699d5ee8
PS
444static int
445splat_taskq_test3(struct file *file, void *arg)
446{
447 int rc;
448
449 rc = splat_taskq_test3_impl(file, arg, B_FALSE);
450 if (rc)
451 return rc;
452
453 rc = splat_taskq_test3_impl(file, arg, B_TRUE);
454
455 return rc;
456}
457
5562e5d1
BB
458/*
459 * Create a taskq and dispatch a large number of tasks to the queue.
460 * Then use taskq_wait() to block until all the tasks complete, then
2f357826
BB
461 * cross check that all the tasks ran by checking the shared atomic
462 * counter which is incremented in the task function.
26f7245c
RC
463 *
464 * First we try with a large 'maxalloc' value, then we try with a small one.
465 * We should not drop tasks when TQ_SLEEP is used in taskq_dispatch(), even
466 * if the number of pending tasks is above maxalloc.
5562e5d1 467 */
7257ec41
BB
468static void
469splat_taskq_test4_func(void *arg)
470{
471 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
472 ASSERT(tq_arg);
473
2f357826 474 atomic_inc(tq_arg->count);
7257ec41
BB
475}
476
477static int
26f7245c 478splat_taskq_test4_common(struct file *file, void *arg, int minalloc,
699d5ee8 479 int maxalloc, int nr_tasks, boolean_t prealloc)
7257ec41
BB
480{
481 taskq_t *tq;
699d5ee8 482 taskqid_t id;
7257ec41 483 splat_taskq_arg_t tq_arg;
699d5ee8 484 taskq_ent_t *tqes;
2f357826 485 atomic_t count;
7257ec41
BB
486 int i, j, rc = 0;
487
699d5ee8
PS
488 tqes = kmalloc(sizeof(*tqes) * nr_tasks, GFP_KERNEL);
489 if (tqes == NULL)
490 return -ENOMEM;
491
492 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
493 "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n",
494 SPLAT_TASKQ_TEST4_NAME,
495 prealloc ? "prealloc" : "dynamic",
496 minalloc, maxalloc, nr_tasks);
62aa81a5 497 if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, defclsyspri,
26f7245c 498 minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
7257ec41
BB
499 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
500 "Taskq '%s' create failed\n",
501 SPLAT_TASKQ_TEST4_NAME);
699d5ee8
PS
502 rc = -EINVAL;
503 goto out_free;
7257ec41
BB
504 }
505
506 tq_arg.file = file;
507 tq_arg.name = SPLAT_TASKQ_TEST4_NAME;
2f357826 508 tq_arg.count = &count;
7257ec41 509
26f7245c 510 for (i = 1; i <= nr_tasks; i *= 2) {
2f357826 511 atomic_set(tq_arg.count, 0);
7257ec41
BB
512 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
513 "Taskq '%s' function '%s' dispatched %d times\n",
514 tq_arg.name, sym2str(splat_taskq_test4_func), i);
515
516 for (j = 0; j < i; j++) {
699d5ee8
PS
517 taskq_init_ent(&tqes[j]);
518
519 if (prealloc) {
520 taskq_dispatch_ent(tq, splat_taskq_test4_func,
521 &tq_arg, TQ_SLEEP, &tqes[j]);
522 id = tqes[j].tqent_id;
523 } else {
524 id = taskq_dispatch(tq, splat_taskq_test4_func,
525 &tq_arg, TQ_SLEEP);
526 }
527
cbba7146 528 if (id == TASKQID_INVALID) {
7257ec41
BB
529 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
530 "Taskq '%s' function '%s' dispatch "
531 "%d failed\n", tq_arg.name,
e05bec80 532 sym2str(splat_taskq_test4_func), j);
7257ec41
BB
533 rc = -EINVAL;
534 goto out;
535 }
536 }
537
538 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
539 "waiting for %d dispatches\n", tq_arg.name, i);
540 taskq_wait(tq);
541 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
542 "%d/%d dispatches finished\n", tq_arg.name,
2f357826
BB
543 atomic_read(&count), i);
544 if (atomic_read(&count) != i) {
7257ec41
BB
545 rc = -ERANGE;
546 goto out;
547
548 }
549 }
550out:
551 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' destroying\n",
552 tq_arg.name);
553 taskq_destroy(tq);
554
699d5ee8
PS
555out_free:
556 kfree(tqes);
557
7257ec41
BB
558 return rc;
559}
560
699d5ee8
PS
561static int
562splat_taskq_test4_impl(struct file *file, void *arg, boolean_t prealloc)
563{
564 int rc;
565
566 rc = splat_taskq_test4_common(file, arg, 50, INT_MAX, 1024, prealloc);
567 if (rc)
568 return rc;
569
570 rc = splat_taskq_test4_common(file, arg, 1, 1, 32, prealloc);
571
572 return rc;
573}
574
575static int
576splat_taskq_test4(struct file *file, void *arg)
26f7245c
RC
577{
578 int rc;
579
699d5ee8 580 rc = splat_taskq_test4_impl(file, arg, B_FALSE);
26f7245c
RC
581 if (rc)
582 return rc;
583
699d5ee8 584 rc = splat_taskq_test4_impl(file, arg, B_TRUE);
26f7245c
RC
585
586 return rc;
587}
588
5562e5d1
BB
589/*
590 * Create a taskq and dispatch a specific sequence of tasks carefully
591 * crafted to validate the order in which tasks are processed. When
592 * there are multiple worker threads each thread will process the
593 * next pending task as soon as it completes its current task. This
594 * means that tasks do not strictly complete in order in which they
595 * were dispatched (increasing task id). This is fine but we need to
a876b030
CD
596 * verify taskq_wait_outstanding() blocks until the passed task id and
597 * all lower task ids complete. We do this by dispatching the following
5562e5d1 598 * specific sequence of tasks each of which block for N time units.
a876b030 599 * We then use taskq_wait_outstanding() to unblock at specific task id and
5562e5d1
BB
600 * verify the only the expected task ids have completed and in the
601 * correct order. The two cases of interest are:
602 *
603 * 1) Task ids larger than the waited for task id can run and
604 * complete as long as there is an available worker thread.
605 * 2) All task ids lower than the waited one must complete before
606 * unblocking even if the waited task id itself has completed.
607 *
608 * The following table shows each task id and how they will be
609 * scheduled. Each rows represent one time unit and each column
a876b030 610 * one of the three worker threads. The places taskq_wait_outstanding()
5562e5d1
BB
611 * must unblock for a specific id are identified as well as the
612 * task ids which must have completed and their order.
613 *
a876b030 614 * +-----+ <--- taskq_wait_outstanding(tq, 8) unblocks
55f10ae5 615 * | | Required Completion Order: 1,2,4,5,3,8,6,7
5562e5d1
BB
616 * +-----+ |
617 * | | |
618 * | | +-----+
619 * | | | 8 |
a876b030 620 * | | +-----+ <--- taskq_wait_outstanding(tq, 3) unblocks
55f10ae5 621 * | | 7 | | Required Completion Order: 1,2,4,5,3
5562e5d1
BB
622 * | +-----+ |
623 * | 6 | | |
624 * +-----+ | |
625 * | | 5 | |
626 * | +-----+ |
627 * | 4 | | |
628 * +-----+ | |
629 * | 1 | 2 | 3 |
630 * +-----+-----+-----+
631 *
632 */
633static void
634splat_taskq_test5_func(void *arg)
635{
636 splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg;
637 splat_taskq_arg_t *tq_arg = tq_id->arg;
638 int factor;
639
640 /* Delays determined by above table */
641 switch (tq_id->id) {
642 default: factor = 0; break;
643 case 1: case 8: factor = 1; break;
644 case 2: case 4: case 5: factor = 2; break;
645 case 6: case 7: factor = 4; break;
646 case 3: factor = 5; break;
647 }
648
649 msleep(factor * 100);
650 splat_vprint(tq_arg->file, tq_arg->name,
651 "Taskqid %d complete for taskq '%s'\n",
652 tq_id->id, tq_arg->name);
653
654 spin_lock(&tq_arg->lock);
655 tq_arg->order[tq_arg->flag] = tq_id->id;
656 tq_arg->flag++;
657 spin_unlock(&tq_arg->lock);
658}
659
660static int
55f10ae5 661splat_taskq_test_order(splat_taskq_arg_t *tq_arg, int *order)
5562e5d1
BB
662{
663 int i, j;
664
665 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
666 if (tq_arg->order[i] != order[i]) {
55f10ae5 667 splat_vprint(tq_arg->file, tq_arg->name,
5562e5d1
BB
668 "Taskq '%s' incorrect completion "
669 "order\n", tq_arg->name);
55f10ae5 670 splat_vprint(tq_arg->file, tq_arg->name,
5562e5d1
BB
671 "%s", "Expected { ");
672
673 for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++)
674 splat_print(tq_arg->file, "%d ", order[j]);
675
676 splat_print(tq_arg->file, "%s", "}\n");
55f10ae5 677 splat_vprint(tq_arg->file, tq_arg->name,
5562e5d1
BB
678 "%s", "Got { ");
679
680 for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++)
681 splat_print(tq_arg->file, "%d ",
682 tq_arg->order[j]);
683
684 splat_print(tq_arg->file, "%s", "}\n");
685 return -EILSEQ;
686 }
687 }
688
55f10ae5 689 splat_vprint(tq_arg->file, tq_arg->name,
5562e5d1
BB
690 "Taskq '%s' validated correct completion order\n",
691 tq_arg->name);
692
693 return 0;
694}
695
696static int
699d5ee8 697splat_taskq_test5_impl(struct file *file, void *arg, boolean_t prealloc)
5562e5d1
BB
698{
699 taskq_t *tq;
700 taskqid_t id;
701 splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX];
702 splat_taskq_arg_t tq_arg;
703 int order1[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,0,0,0 };
704 int order2[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,8,6,7 };
94ff5d38 705 taskq_ent_t *tqes;
5562e5d1
BB
706 int i, rc = 0;
707
94ff5d38
BB
708 tqes = kmem_alloc(sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX, KM_SLEEP);
709 memset(tqes, 0, sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX);
710
699d5ee8
PS
711 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
712 "Taskq '%s' creating (%s dispatch)\n",
713 SPLAT_TASKQ_TEST5_NAME,
714 prealloc ? "prealloc" : "dynamic");
62aa81a5 715 if ((tq = taskq_create(SPLAT_TASKQ_TEST5_NAME, 3, defclsyspri,
5562e5d1
BB
716 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
717 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
718 "Taskq '%s' create failed\n",
719 SPLAT_TASKQ_TEST5_NAME);
720 return -EINVAL;
721 }
722
723 tq_arg.flag = 0;
724 memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX);
725 spin_lock_init(&tq_arg.lock);
726 tq_arg.file = file;
727 tq_arg.name = SPLAT_TASKQ_TEST5_NAME;
728
729 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
699d5ee8
PS
730 taskq_init_ent(&tqes[i]);
731
5562e5d1
BB
732 tq_id[i].id = i + 1;
733 tq_id[i].arg = &tq_arg;
734
699d5ee8
PS
735 if (prealloc) {
736 taskq_dispatch_ent(tq, splat_taskq_test5_func,
737 &tq_id[i], TQ_SLEEP, &tqes[i]);
738 id = tqes[i].tqent_id;
739 } else {
740 id = taskq_dispatch(tq, splat_taskq_test5_func,
741 &tq_id[i], TQ_SLEEP);
742 }
743
cbba7146 744 if (id == TASKQID_INVALID) {
5562e5d1
BB
745 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
746 "Taskq '%s' function '%s' dispatch failed\n",
747 tq_arg.name, sym2str(splat_taskq_test5_func));
748 rc = -EINVAL;
749 goto out;
750 }
751
752 if (tq_id[i].id != id) {
753 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
754 "Taskq '%s' expected taskqid %d got %d\n",
755 tq_arg.name, (int)tq_id[i].id, (int)id);
756 rc = -EINVAL;
757 goto out;
758 }
759 }
760
761 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' "
762 "waiting for taskqid %d completion\n", tq_arg.name, 3);
a876b030 763 taskq_wait_outstanding(tq, 3);
55f10ae5 764 if ((rc = splat_taskq_test_order(&tq_arg, order1)))
5562e5d1
BB
765 goto out;
766
767 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' "
768 "waiting for taskqid %d completion\n", tq_arg.name, 8);
a876b030 769 taskq_wait_outstanding(tq, 8);
55f10ae5 770 rc = splat_taskq_test_order(&tq_arg, order2);
5562e5d1
BB
771
772out:
773 splat_vprint(file, SPLAT_TASKQ_TEST5_NAME,
774 "Taskq '%s' destroying\n", tq_arg.name);
775 taskq_destroy(tq);
776
94ff5d38
BB
777 kmem_free(tqes, sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX);
778
5562e5d1
BB
779 return rc;
780}
781
699d5ee8
PS
782static int
783splat_taskq_test5(struct file *file, void *arg)
784{
785 int rc;
786
787 rc = splat_taskq_test5_impl(file, arg, B_FALSE);
788 if (rc)
789 return rc;
790
791 rc = splat_taskq_test5_impl(file, arg, B_TRUE);
792
793 return rc;
794}
795
55f10ae5
NB
796/*
797 * Create a single task queue with three threads. Dispatch 8 tasks,
798 * setting TQ_FRONT on only the last three. Sleep after
799 * dispatching tasks 1-3 to ensure they will run and hold the threads
800 * busy while we dispatch the remaining tasks. Verify that tasks 6-8
801 * run before task 4-5.
802 *
803 * The following table shows each task id and how they will be
804 * scheduled. Each rows represent one time unit and each column
805 * one of the three worker threads.
806 *
9b88fa16
SJ
807 * NB: The Horizontal Line is the LAST Time unit consumed by the Task,
808 * and must be included in the factor calculation.
809 * T
810 * 17-> +-----+
811 * 16 | T6 |
812 * 15-> +-----+ |
813 * 14 | T6 | |
814 * 13-> | | 5 +-----+
815 * 12 | | | T6 |
816 * 11-> | +-----| |
817 * 10 | 4 | T6 | |
818 * 9-> +-----+ | 8 |
819 * 8 | T5 | | |
820 * 7-> | | 7 +-----+
821 * 6 | | | T7 |
822 * 5-> | +-----+ |
823 * 4 | 6 | T5 | |
824 * 3-> +-----+ | |
825 * 2 | T3 | | |
826 * 1 | 1 | 2 | 3 |
827 * 0 +-----+-----+-----+
55f10ae5
NB
828 *
829 */
830static void
831splat_taskq_test6_func(void *arg)
832{
9b88fa16
SJ
833 /* Delays determined by above table */
834 static const int factor[SPLAT_TASKQ_ORDER_MAX+1] = {0,3,5,7,6,6,5,6,6};
835
55f10ae5
NB
836 splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg;
837 splat_taskq_arg_t *tq_arg = tq_id->arg;
55f10ae5
NB
838
839 splat_vprint(tq_arg->file, tq_arg->name,
9b88fa16 840 "Taskqid %d starting for taskq '%s'\n",
55f10ae5
NB
841 tq_id->id, tq_arg->name);
842
9b88fa16
SJ
843 if (tq_id->id < SPLAT_TASKQ_ORDER_MAX+1) {
844 msleep(factor[tq_id->id] * 50);
845 }
846
55f10ae5
NB
847 spin_lock(&tq_arg->lock);
848 tq_arg->order[tq_arg->flag] = tq_id->id;
849 tq_arg->flag++;
81857a34
BB
850 spin_unlock(&tq_arg->lock);
851
9b88fa16
SJ
852 splat_vprint(tq_arg->file, tq_arg->name,
853 "Taskqid %d complete for taskq '%s'\n",
854 tq_id->id, tq_arg->name);
55f10ae5
NB
855}
856
857static int
699d5ee8 858splat_taskq_test6_impl(struct file *file, void *arg, boolean_t prealloc)
55f10ae5
NB
859{
860 taskq_t *tq;
861 taskqid_t id;
862 splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX];
863 splat_taskq_arg_t tq_arg;
864 int order[SPLAT_TASKQ_ORDER_MAX] = { 1,2,3,6,7,8,4,5 };
a5a98e72 865 taskq_ent_t *tqes;
55f10ae5
NB
866 int i, rc = 0;
867 uint_t tflags;
868
a5a98e72
BB
869 tqes = kmem_alloc(sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX, KM_SLEEP);
870 memset(tqes, 0, sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX);
871
699d5ee8
PS
872 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
873 "Taskq '%s' creating (%s dispatch)\n",
874 SPLAT_TASKQ_TEST6_NAME,
875 prealloc ? "prealloc" : "dynamic");
62aa81a5 876 if ((tq = taskq_create(SPLAT_TASKQ_TEST6_NAME, 3, defclsyspri,
55f10ae5
NB
877 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
878 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
879 "Taskq '%s' create failed\n",
880 SPLAT_TASKQ_TEST6_NAME);
881 return -EINVAL;
882 }
883
884 tq_arg.flag = 0;
885 memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX);
886 spin_lock_init(&tq_arg.lock);
887 tq_arg.file = file;
888 tq_arg.name = SPLAT_TASKQ_TEST6_NAME;
889
890 for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) {
699d5ee8
PS
891 taskq_init_ent(&tqes[i]);
892
55f10ae5
NB
893 tq_id[i].id = i + 1;
894 tq_id[i].arg = &tq_arg;
895 tflags = TQ_SLEEP;
896 if (i > 4)
897 tflags |= TQ_FRONT;
898
699d5ee8
PS
899 if (prealloc) {
900 taskq_dispatch_ent(tq, splat_taskq_test6_func,
901 &tq_id[i], tflags, &tqes[i]);
902 id = tqes[i].tqent_id;
903 } else {
904 id = taskq_dispatch(tq, splat_taskq_test6_func,
905 &tq_id[i], tflags);
906 }
907
cbba7146 908 if (id == TASKQID_INVALID) {
55f10ae5
NB
909 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
910 "Taskq '%s' function '%s' dispatch failed\n",
911 tq_arg.name, sym2str(splat_taskq_test6_func));
912 rc = -EINVAL;
913 goto out;
914 }
915
916 if (tq_id[i].id != id) {
917 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
918 "Taskq '%s' expected taskqid %d got %d\n",
919 tq_arg.name, (int)tq_id[i].id, (int)id);
920 rc = -EINVAL;
921 goto out;
922 }
923 /* Sleep to let tasks 1-3 start executing. */
924 if ( i == 2 )
925 msleep(100);
926 }
927
928 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' "
929 "waiting for taskqid %d completion\n", tq_arg.name,
930 SPLAT_TASKQ_ORDER_MAX);
a876b030 931 taskq_wait_outstanding(tq, SPLAT_TASKQ_ORDER_MAX);
55f10ae5
NB
932 rc = splat_taskq_test_order(&tq_arg, order);
933
934out:
935 splat_vprint(file, SPLAT_TASKQ_TEST6_NAME,
936 "Taskq '%s' destroying\n", tq_arg.name);
937 taskq_destroy(tq);
938
a5a98e72
BB
939 kmem_free(tqes, sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX);
940
55f10ae5
NB
941 return rc;
942}
943
699d5ee8
PS
944static int
945splat_taskq_test6(struct file *file, void *arg)
946{
947 int rc;
948
949 rc = splat_taskq_test6_impl(file, arg, B_FALSE);
950 if (rc)
951 return rc;
952
953 rc = splat_taskq_test6_impl(file, arg, B_TRUE);
954
955 return rc;
956}
957
ac1e5b60
PS
958static void
959splat_taskq_test7_func(void *arg)
960{
961 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
962 taskqid_t id;
963
964 ASSERT(tq_arg);
965
966 if (tq_arg->depth >= SPLAT_TASKQ_DEPTH_MAX)
967 return;
968
969 tq_arg->depth++;
970
971 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST7_NAME,
972 "Taskq '%s' function '%s' dispatching (depth = %u)\n",
973 tq_arg->name, sym2str(splat_taskq_test7_func),
974 tq_arg->depth);
975
699d5ee8
PS
976 if (tq_arg->tqe) {
977 VERIFY(taskq_empty_ent(tq_arg->tqe));
978 taskq_dispatch_ent(tq_arg->tq, splat_taskq_test7_func,
979 tq_arg, TQ_SLEEP, tq_arg->tqe);
980 id = tq_arg->tqe->tqent_id;
981 } else {
982 id = taskq_dispatch(tq_arg->tq, splat_taskq_test7_func,
983 tq_arg, TQ_SLEEP);
984 }
985
cbba7146 986 if (id == TASKQID_INVALID) {
ac1e5b60
PS
987 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST7_NAME,
988 "Taskq '%s' function '%s' dispatch failed "
989 "(depth = %u)\n", tq_arg->name,
990 sym2str(splat_taskq_test7_func), tq_arg->depth);
991 tq_arg->flag = -EINVAL;
992 return;
993 }
994}
995
996static int
699d5ee8 997splat_taskq_test7_impl(struct file *file, void *arg, boolean_t prealloc)
ac1e5b60
PS
998{
999 taskq_t *tq;
03a78353
BB
1000 splat_taskq_arg_t *tq_arg;
1001 taskq_ent_t *tqe;
1002 int error;
ac1e5b60
PS
1003
1004 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
699d5ee8
PS
1005 "Taskq '%s' creating (%s dispatch)\n",
1006 SPLAT_TASKQ_TEST7_NAME,
1007 prealloc ? "prealloc" : "dynamic");
62aa81a5 1008 if ((tq = taskq_create(SPLAT_TASKQ_TEST7_NAME, 1, defclsyspri,
ac1e5b60
PS
1009 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
1010 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
1011 "Taskq '%s' create failed\n",
1012 SPLAT_TASKQ_TEST7_NAME);
1013 return -EINVAL;
1014 }
1015
03a78353
BB
1016 tq_arg = kmem_alloc(sizeof (splat_taskq_arg_t), KM_SLEEP);
1017 tqe = kmem_alloc(sizeof (taskq_ent_t), KM_SLEEP);
1018
1019 tq_arg->depth = 0;
1020 tq_arg->flag = 0;
1021 tq_arg->id = 0;
1022 tq_arg->file = file;
1023 tq_arg->name = SPLAT_TASKQ_TEST7_NAME;
1024 tq_arg->tq = tq;
ac1e5b60 1025
699d5ee8 1026 if (prealloc) {
03a78353
BB
1027 taskq_init_ent(tqe);
1028 tq_arg->tqe = tqe;
699d5ee8 1029 } else {
03a78353 1030 tq_arg->tqe = NULL;
699d5ee8
PS
1031 }
1032
03a78353 1033 splat_taskq_test7_func(tq_arg);
ac1e5b60 1034
03a78353 1035 if (tq_arg->flag == 0) {
ac1e5b60 1036 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
03a78353 1037 "Taskq '%s' waiting\n", tq_arg->name);
a876b030 1038 taskq_wait_outstanding(tq, SPLAT_TASKQ_DEPTH_MAX);
ac1e5b60
PS
1039 }
1040
03a78353
BB
1041 error = (tq_arg->depth == SPLAT_TASKQ_DEPTH_MAX ? 0 : -EINVAL);
1042
ea5f1a20
CC
1043 splat_vprint(file, SPLAT_TASKQ_TEST7_NAME,
1044 "Taskq '%s' destroying\n", tq_arg->name);
1045
03a78353
BB
1046 kmem_free(tqe, sizeof (taskq_ent_t));
1047 kmem_free(tq_arg, sizeof (splat_taskq_arg_t));
1048
ac1e5b60
PS
1049 taskq_destroy(tq);
1050
03a78353 1051 return (error);
ac1e5b60
PS
1052}
1053
699d5ee8
PS
1054static int
1055splat_taskq_test7(struct file *file, void *arg)
1056{
1057 int rc;
1058
1059 rc = splat_taskq_test7_impl(file, arg, B_FALSE);
1060 if (rc)
f7a973d9 1061 return (rc);
699d5ee8
PS
1062
1063 rc = splat_taskq_test7_impl(file, arg, B_TRUE);
1064
f7a973d9 1065 return (rc);
699d5ee8
PS
1066}
1067
cf5d23fa 1068static void
f7a973d9 1069splat_taskq_throughput_func(void *arg)
cf5d23fa
NB
1070{
1071 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
1072 ASSERT(tq_arg);
1073
2f357826 1074 atomic_inc(tq_arg->count);
cf5d23fa
NB
1075}
1076
cf5d23fa 1077static int
f7a973d9
BB
1078splat_taskq_throughput(struct file *file, void *arg, const char *name,
1079 int nthreads, int minalloc, int maxalloc, int flags, int tasks,
1080 struct timespec *delta)
cf5d23fa
NB
1081{
1082 taskq_t *tq;
1083 taskqid_t id;
1084 splat_taskq_arg_t tq_arg;
1085 taskq_ent_t **tqes;
2f357826 1086 atomic_t count;
f7a973d9 1087 struct timespec start, stop;
cf5d23fa
NB
1088 int i, j, rc = 0;
1089
f7a973d9 1090 tqes = vmalloc(sizeof (*tqes) * tasks);
cf5d23fa 1091 if (tqes == NULL)
f7a973d9
BB
1092 return (-ENOMEM);
1093
1094 memset(tqes, 0, sizeof (*tqes) * tasks);
1095
1096 splat_vprint(file, name, "Taskq '%s' creating (%d/%d/%d/%d)\n",
1097 name, nthreads, minalloc, maxalloc, tasks);
62aa81a5 1098 if ((tq = taskq_create(name, nthreads, defclsyspri,
f7a973d9
BB
1099 minalloc, maxalloc, flags)) == NULL) {
1100 splat_vprint(file, name, "Taskq '%s' create failed\n", name);
cf5d23fa
NB
1101 rc = -EINVAL;
1102 goto out_free;
1103 }
1104
1105 tq_arg.file = file;
f7a973d9 1106 tq_arg.name = name;
2f357826
BB
1107 tq_arg.count = &count;
1108 atomic_set(tq_arg.count, 0);
cf5d23fa 1109
f7a973d9
BB
1110 getnstimeofday(&start);
1111
1112 for (i = 0; i < tasks; i++) {
1113 tqes[i] = kmalloc(sizeof (taskq_ent_t), GFP_KERNEL);
cf5d23fa
NB
1114 if (tqes[i] == NULL) {
1115 rc = -ENOMEM;
1116 goto out;
1117 }
cf5d23fa 1118
f7a973d9
BB
1119 taskq_init_ent(tqes[i]);
1120 taskq_dispatch_ent(tq, splat_taskq_throughput_func,
1121 &tq_arg, TQ_SLEEP, tqes[i]);
cf5d23fa
NB
1122 id = tqes[i]->tqent_id;
1123
cbba7146 1124 if (id == TASKQID_INVALID) {
f7a973d9
BB
1125 splat_vprint(file, name, "Taskq '%s' function '%s' "
1126 "dispatch %d failed\n", tq_arg.name,
1127 sym2str(splat_taskq_throughput_func), i);
1128 rc = -EINVAL;
1129 goto out;
cf5d23fa
NB
1130 }
1131 }
1132
f7a973d9
BB
1133 splat_vprint(file, name, "Taskq '%s' waiting for %d dispatches\n",
1134 tq_arg.name, tasks);
1135
cf5d23fa 1136 taskq_wait(tq);
cf5d23fa 1137
f7a973d9
BB
1138 if (delta != NULL) {
1139 getnstimeofday(&stop);
1140 *delta = timespec_sub(stop, start);
1141 }
1142
1143 splat_vprint(file, name, "Taskq '%s' %d/%d dispatches finished\n",
1144 tq_arg.name, atomic_read(tq_arg.count), tasks);
1145
1146 if (atomic_read(tq_arg.count) != tasks)
cf5d23fa
NB
1147 rc = -ERANGE;
1148
1149out:
f7a973d9 1150 splat_vprint(file, name, "Taskq '%s' destroying\n", tq_arg.name);
cf5d23fa
NB
1151 taskq_destroy(tq);
1152out_free:
f7a973d9 1153 for (j = 0; j < tasks && tqes[j] != NULL; j++)
cf5d23fa 1154 kfree(tqes[j]);
f7a973d9 1155
cf5d23fa
NB
1156 vfree(tqes);
1157
f7a973d9 1158 return (rc);
cf5d23fa
NB
1159}
1160
f7a973d9
BB
1161/*
1162 * Create a taskq with 100 threads and dispatch a huge number of trivial
1163 * tasks to generate contention on tq->tq_lock. This test should always
1164 * pass. The purpose is to provide a benchmark for measuring the
1165 * effectiveness of taskq optimizations.
1166 */
1167#define TEST8_NUM_TASKS 0x20000
1168#define TEST8_THREADS_PER_TASKQ 100
1169
cf5d23fa
NB
1170static int
1171splat_taskq_test8(struct file *file, void *arg)
1172{
f7a973d9
BB
1173 return (splat_taskq_throughput(file, arg,
1174 SPLAT_TASKQ_TEST8_NAME, TEST8_THREADS_PER_TASKQ,
1175 1, INT_MAX, TASKQ_PREPOPULATE, TEST8_NUM_TASKS, NULL));
cf5d23fa
NB
1176}
1177
2f357826
BB
1178/*
1179 * Create a taskq and dispatch a number of delayed tasks to the queue.
1180 * For each task verify that it was run no early than requested.
1181 */
1182static void
1183splat_taskq_test9_func(void *arg)
1184{
1185 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
1186 ASSERT(tq_arg);
1187
545e9ac0 1188 if (ddi_time_after_eq(ddi_get_lbolt(), tq_arg->expire))
2f357826
BB
1189 atomic_inc(tq_arg->count);
1190
1191 kmem_free(tq_arg, sizeof(splat_taskq_arg_t));
1192}
1193
1194static int
1195splat_taskq_test9(struct file *file, void *arg)
1196{
1197 taskq_t *tq;
1198 atomic_t count;
1199 int i, rc = 0;
1200 int minalloc = 1;
1201 int maxalloc = 10;
1202 int nr_tasks = 100;
1203
1204 splat_vprint(file, SPLAT_TASKQ_TEST9_NAME,
1205 "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n",
1206 SPLAT_TASKQ_TEST9_NAME, "delay", minalloc, maxalloc, nr_tasks);
62aa81a5 1207 if ((tq = taskq_create(SPLAT_TASKQ_TEST9_NAME, 3, defclsyspri,
2f357826
BB
1208 minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
1209 splat_vprint(file, SPLAT_TASKQ_TEST9_NAME,
1210 "Taskq '%s' create failed\n", SPLAT_TASKQ_TEST9_NAME);
1211 return -EINVAL;
1212 }
1213
1214 atomic_set(&count, 0);
1215
1216 for (i = 1; i <= nr_tasks; i++) {
1217 splat_taskq_arg_t *tq_arg;
1218 taskqid_t id;
1219 uint32_t rnd;
1220
1221 /* A random timeout in jiffies of at most 5 seconds */
1222 get_random_bytes((void *)&rnd, 4);
1223 rnd = rnd % (5 * HZ);
1224
1225 tq_arg = kmem_alloc(sizeof(splat_taskq_arg_t), KM_SLEEP);
1226 tq_arg->file = file;
1227 tq_arg->name = SPLAT_TASKQ_TEST9_NAME;
1228 tq_arg->expire = ddi_get_lbolt() + rnd;
1229 tq_arg->count = &count;
1230
1231 splat_vprint(file, SPLAT_TASKQ_TEST9_NAME,
1232 "Taskq '%s' delay dispatch %u jiffies\n",
1233 SPLAT_TASKQ_TEST9_NAME, rnd);
1234
1235 id = taskq_dispatch_delay(tq, splat_taskq_test9_func,
1236 tq_arg, TQ_SLEEP, ddi_get_lbolt() + rnd);
1237
cbba7146 1238 if (id == TASKQID_INVALID) {
2f357826
BB
1239 splat_vprint(file, SPLAT_TASKQ_TEST9_NAME,
1240 "Taskq '%s' delay dispatch failed\n",
1241 SPLAT_TASKQ_TEST9_NAME);
1242 kmem_free(tq_arg, sizeof(splat_taskq_arg_t));
1243 taskq_wait(tq);
1244 rc = -EINVAL;
1245 goto out;
1246 }
1247 }
1248
1249 splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' waiting for "
1250 "%d delay dispatches\n", SPLAT_TASKQ_TEST9_NAME, nr_tasks);
1251
1252 taskq_wait(tq);
1253 if (atomic_read(&count) != nr_tasks)
1254 rc = -ERANGE;
1255
1256 splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' %d/%d delay "
1257 "dispatches finished on time\n", SPLAT_TASKQ_TEST9_NAME,
1258 atomic_read(&count), nr_tasks);
1259 splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' destroying\n",
1260 SPLAT_TASKQ_TEST9_NAME);
1261out:
1262 taskq_destroy(tq);
1263
1264 return rc;
1265}
1266
3238e717
BB
1267/*
1268 * Create a taskq and dispatch then cancel tasks in the queue.
1269 */
1270static void
1271splat_taskq_test10_func(void *arg)
1272{
1273 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
1274 uint8_t rnd;
1275
545e9ac0 1276 if (ddi_time_after_eq(ddi_get_lbolt(), tq_arg->expire))
3238e717
BB
1277 atomic_inc(tq_arg->count);
1278
1279 /* Randomly sleep to further perturb the system */
1280 get_random_bytes((void *)&rnd, 1);
1281 msleep(1 + (rnd % 9));
1282}
1283
1284static int
1285splat_taskq_test10(struct file *file, void *arg)
1286{
1287 taskq_t *tq;
1288 splat_taskq_arg_t **tqas;
1289 atomic_t count;
1290 int i, j, rc = 0;
1291 int minalloc = 1;
1292 int maxalloc = 10;
1293 int nr_tasks = 100;
1294 int canceled = 0;
1295 int completed = 0;
1296 int blocked = 0;
545e9ac0 1297 clock_t start, cancel;
3238e717
BB
1298
1299 tqas = vmalloc(sizeof(*tqas) * nr_tasks);
1300 if (tqas == NULL)
1301 return -ENOMEM;
1302 memset(tqas, 0, sizeof(*tqas) * nr_tasks);
1303
1304 splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
1305 "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n",
1306 SPLAT_TASKQ_TEST10_NAME, "delay", minalloc, maxalloc, nr_tasks);
62aa81a5 1307 if ((tq = taskq_create(SPLAT_TASKQ_TEST10_NAME, 3, defclsyspri,
3238e717
BB
1308 minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) {
1309 splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
1310 "Taskq '%s' create failed\n", SPLAT_TASKQ_TEST10_NAME);
1311 rc = -EINVAL;
1312 goto out_free;
1313 }
1314
1315 atomic_set(&count, 0);
1316
1317 for (i = 0; i < nr_tasks; i++) {
1318 splat_taskq_arg_t *tq_arg;
1319 uint32_t rnd;
1320
1321 /* A random timeout in jiffies of at most 5 seconds */
1322 get_random_bytes((void *)&rnd, 4);
1323 rnd = rnd % (5 * HZ);
1324
1325 tq_arg = kmem_alloc(sizeof(splat_taskq_arg_t), KM_SLEEP);
1326 tq_arg->file = file;
1327 tq_arg->name = SPLAT_TASKQ_TEST10_NAME;
1328 tq_arg->count = &count;
1329 tqas[i] = tq_arg;
1330
1331 /*
1332 * Dispatch every 1/3 one immediately to mix it up, the cancel
1333 * code is inherently racy and we want to try and provoke any
1334 * subtle concurrently issues.
1335 */
1336 if ((i % 3) == 0) {
1337 tq_arg->expire = ddi_get_lbolt();
1338 tq_arg->id = taskq_dispatch(tq, splat_taskq_test10_func,
1339 tq_arg, TQ_SLEEP);
1340 } else {
1341 tq_arg->expire = ddi_get_lbolt() + rnd;
1342 tq_arg->id = taskq_dispatch_delay(tq,
1343 splat_taskq_test10_func,
1344 tq_arg, TQ_SLEEP, ddi_get_lbolt() + rnd);
1345 }
1346
cbba7146 1347 if (tq_arg->id == TASKQID_INVALID) {
3238e717
BB
1348 splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
1349 "Taskq '%s' dispatch failed\n",
1350 SPLAT_TASKQ_TEST10_NAME);
1351 kmem_free(tq_arg, sizeof(splat_taskq_arg_t));
1352 taskq_wait(tq);
1353 rc = -EINVAL;
1354 goto out;
1355 } else {
1356 splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
1357 "Taskq '%s' dispatch %lu in %lu jiffies\n",
1358 SPLAT_TASKQ_TEST10_NAME, (unsigned long)tq_arg->id,
1359 !(i % 3) ? 0 : tq_arg->expire - ddi_get_lbolt());
1360 }
1361 }
1362
1363 /*
1364 * Start randomly canceling tasks for the duration of the test. We
1365 * happen to know the valid task id's will be in the range 1..nr_tasks
1366 * because the taskq is private and was just created. However, we
1367 * have no idea of a particular task has already executed or not.
1368 */
1369 splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' randomly "
1370 "canceling task ids\n", SPLAT_TASKQ_TEST10_NAME);
1371
1372 start = ddi_get_lbolt();
1373 i = 0;
1374
545e9ac0 1375 while (ddi_time_before(ddi_get_lbolt(), start + 5 * HZ)) {
3238e717
BB
1376 taskqid_t id;
1377 uint32_t rnd;
1378
1379 i++;
1380 cancel = ddi_get_lbolt();
1381 get_random_bytes((void *)&rnd, 4);
1382 id = 1 + (rnd % nr_tasks);
1383 rc = taskq_cancel_id(tq, id);
1384
1385 /*
1386 * Keep track of the results of the random cancels.
1387 */
1388 if (rc == 0) {
1389 canceled++;
1390 } else if (rc == ENOENT) {
1391 completed++;
1392 } else if (rc == EBUSY) {
1393 blocked++;
1394 } else {
1395 rc = -EINVAL;
1396 break;
1397 }
1398
1399 /*
1400 * Verify we never get blocked to long in taskq_cancel_id().
1401 * The worst case is 10ms if we happen to cancel the task
1402 * which is currently executing. We allow a factor of 2x.
1403 */
1404 if (ddi_get_lbolt() - cancel > HZ / 50) {
1405 splat_vprint(file, SPLAT_TASKQ_TEST10_NAME,
1406 "Taskq '%s' cancel for %lu took %lu\n",
1407 SPLAT_TASKQ_TEST10_NAME, (unsigned long)id,
1408 ddi_get_lbolt() - cancel);
1409 rc = -ETIMEDOUT;
1410 break;
1411 }
1412
1413 get_random_bytes((void *)&rnd, 4);
1414 msleep(1 + (rnd % 100));
1415 rc = 0;
1416 }
1417
1418 taskq_wait(tq);
1419
1420 /*
1421 * Cross check the results of taskq_cancel_id() with the number of
1422 * times the dispatched function actually ran successfully.
1423 */
1424 if ((rc == 0) && (nr_tasks - canceled != atomic_read(&count)))
1425 rc = -EDOM;
1426
1427 splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' %d attempts, "
1428 "%d canceled, %d completed, %d blocked, %d/%d tasks run\n",
1429 SPLAT_TASKQ_TEST10_NAME, i, canceled, completed, blocked,
1430 atomic_read(&count), nr_tasks);
1431 splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' destroying %d\n",
1432 SPLAT_TASKQ_TEST10_NAME, rc);
1433out:
1434 taskq_destroy(tq);
1435out_free:
1436 for (j = 0; j < nr_tasks && tqas[j] != NULL; j++)
1437 kmem_free(tqas[j], sizeof(splat_taskq_arg_t));
1438 vfree(tqas);
1439
1440 return rc;
1441}
1442
f7a973d9
BB
1443/*
1444 * Create a dynamic taskq with 100 threads and dispatch a huge number of
1445 * trivial tasks. This will cause the taskq to grow quickly to its max
1446 * thread count. This test should always pass. The purpose is to provide
1447 * a benchmark for measuring the performance of dynamic taskqs.
1448 */
1449#define TEST11_NUM_TASKS 100000
1450#define TEST11_THREADS_PER_TASKQ 100
1451
1452static int
1453splat_taskq_test11(struct file *file, void *arg)
1454{
1455 struct timespec normal, dynamic;
1456 int error;
1457
1458 error = splat_taskq_throughput(file, arg, SPLAT_TASKQ_TEST11_NAME,
1459 TEST11_THREADS_PER_TASKQ, 1, INT_MAX,
1460 TASKQ_PREPOPULATE, TEST11_NUM_TASKS, &normal);
1461 if (error)
1462 return (error);
1463
1464 error = splat_taskq_throughput(file, arg, SPLAT_TASKQ_TEST11_NAME,
1465 TEST11_THREADS_PER_TASKQ, 1, INT_MAX,
1466 TASKQ_PREPOPULATE | TASKQ_DYNAMIC, TEST11_NUM_TASKS, &dynamic);
1467 if (error)
1468 return (error);
1469
1470 splat_vprint(file, SPLAT_TASKQ_TEST11_NAME,
1471 "Timing taskq_wait(): normal=%ld.%09lds, dynamic=%ld.%09lds\n",
1472 normal.tv_sec, normal.tv_nsec,
1473 dynamic.tv_sec, dynamic.tv_nsec);
1474
1475 /* A 10x increase in runtime is used to indicate a core problem. */
cbba7146
U
1476 if (((int64_t)dynamic.tv_sec * NANOSEC + (int64_t)dynamic.tv_nsec) >
1477 (((int64_t)normal.tv_sec * NANOSEC + (int64_t)normal.tv_nsec) * 10))
f7a973d9
BB
1478 error = -ETIME;
1479
1480 return (error);
1481}
1482
7c50328b 1483splat_subsystem_t *
1484splat_taskq_init(void)
f1ca4da6 1485{
7c50328b 1486 splat_subsystem_t *sub;
f1ca4da6 1487
1488 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
1489 if (sub == NULL)
1490 return NULL;
1491
1492 memset(sub, 0, sizeof(*sub));
7c50328b 1493 strncpy(sub->desc.name, SPLAT_TASKQ_NAME, SPLAT_NAME_SIZE);
1494 strncpy(sub->desc.desc, SPLAT_TASKQ_DESC, SPLAT_DESC_SIZE);
f1ca4da6 1495 INIT_LIST_HEAD(&sub->subsystem_list);
1496 INIT_LIST_HEAD(&sub->test_list);
1497 spin_lock_init(&sub->test_lock);
7c50328b 1498 sub->desc.id = SPLAT_SUBSYSTEM_TASKQ;
f1ca4da6 1499
7c50328b 1500 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST1_NAME, SPLAT_TASKQ_TEST1_DESC,
1501 SPLAT_TASKQ_TEST1_ID, splat_taskq_test1);
1502 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST2_NAME, SPLAT_TASKQ_TEST2_DESC,
1503 SPLAT_TASKQ_TEST2_ID, splat_taskq_test2);
e9cb2b4f
BB
1504 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST3_NAME, SPLAT_TASKQ_TEST3_DESC,
1505 SPLAT_TASKQ_TEST3_ID, splat_taskq_test3);
7257ec41
BB
1506 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST4_NAME, SPLAT_TASKQ_TEST4_DESC,
1507 SPLAT_TASKQ_TEST4_ID, splat_taskq_test4);
5562e5d1
BB
1508 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST5_NAME, SPLAT_TASKQ_TEST5_DESC,
1509 SPLAT_TASKQ_TEST5_ID, splat_taskq_test5);
55f10ae5
NB
1510 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST6_NAME, SPLAT_TASKQ_TEST6_DESC,
1511 SPLAT_TASKQ_TEST6_ID, splat_taskq_test6);
ac1e5b60
PS
1512 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST7_NAME, SPLAT_TASKQ_TEST7_DESC,
1513 SPLAT_TASKQ_TEST7_ID, splat_taskq_test7);
cf5d23fa
NB
1514 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST8_NAME, SPLAT_TASKQ_TEST8_DESC,
1515 SPLAT_TASKQ_TEST8_ID, splat_taskq_test8);
2f357826
BB
1516 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST9_NAME, SPLAT_TASKQ_TEST9_DESC,
1517 SPLAT_TASKQ_TEST9_ID, splat_taskq_test9);
3238e717
BB
1518 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST10_NAME, SPLAT_TASKQ_TEST10_DESC,
1519 SPLAT_TASKQ_TEST10_ID, splat_taskq_test10);
f7a973d9
BB
1520 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST11_NAME, SPLAT_TASKQ_TEST11_DESC,
1521 SPLAT_TASKQ_TEST11_ID, splat_taskq_test11);
f1ca4da6 1522
1523 return sub;
1524}
1525
1526void
7c50328b 1527splat_taskq_fini(splat_subsystem_t *sub)
f1ca4da6 1528{
1529 ASSERT(sub);
f7a973d9 1530 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST11_ID);
3238e717 1531 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST10_ID);
2f357826 1532 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST9_ID);
cf5d23fa 1533 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST8_ID);
ac1e5b60 1534 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST7_ID);
ed948fa7 1535 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST6_ID);
5562e5d1 1536 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST5_ID);
7257ec41 1537 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST4_ID);
e9cb2b4f 1538 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST3_ID);
7c50328b 1539 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST2_ID);
1540 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST1_ID);
f1ca4da6 1541
1542 kfree(sub);
1543}
1544
1545int
7c50328b 1546splat_taskq_id(void) {
1547 return SPLAT_SUBSYSTEM_TASKQ;
f1ca4da6 1548}