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