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