]>
Commit | Line | Data |
---|---|---|
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 | 82 | typedef 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 |
96 | typedef 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 | */ |
105 | static void | |
e9cb2b4f | 106 | splat_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 | ||
117 | static int | |
699d5ee8 | 118 | splat_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 |
179 | static int |
180 | splat_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 | */ |
199 | static void | |
7c50328b | 200 | splat_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 | ||
213 | static void | |
7c50328b | 214 | splat_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 | 230 | static int |
699d5ee8 | 231 | splat_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 |
350 | out: |
351 | if (func1_tqes) | |
352 | kfree(func1_tqes); | |
353 | ||
354 | if (func2_tqes) | |
355 | kfree(func2_tqes); | |
356 | ||
357 | return rc; | |
358 | } | |
359 | ||
360 | static int | |
361 | splat_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 | */ |
377 | static int | |
699d5ee8 | 378 | splat_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 |
428 | static int |
429 | splat_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 |
452 | static void |
453 | splat_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 | ||
461 | static int | |
26f7245c | 462 | splat_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 | } | |
534 | out: | |
535 | splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' destroying\n", | |
536 | tq_arg.name); | |
537 | taskq_destroy(tq); | |
538 | ||
699d5ee8 PS |
539 | out_free: |
540 | kfree(tqes); | |
541 | ||
7257ec41 BB |
542 | return rc; |
543 | } | |
544 | ||
699d5ee8 PS |
545 | static int |
546 | splat_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 | ||
559 | static int | |
560 | splat_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 | */ | |
617 | static void | |
618 | splat_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 | ||
644 | static int | |
55f10ae5 | 645 | splat_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 | ||
680 | static int | |
699d5ee8 | 681 | splat_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 | |
756 | out: | |
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 |
766 | static int |
767 | splat_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 | */ | |
814 | static void | |
815 | splat_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 | ||
841 | static int | |
699d5ee8 | 842 | splat_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 | ||
918 | out: | |
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 |
928 | static int |
929 | splat_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 |
942 | static void |
943 | splat_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 | ||
980 | static int | |
699d5ee8 | 981 | splat_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 |
1037 | static int |
1038 | splat_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 | */ | |
1057 | static void | |
1058 | splat_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 | ||
1069 | static int | |
1070 | splat_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 | ||
1138 | out: | |
1139 | splat_vprint(file, SPLAT_TASKQ_TEST8_NAME, "Taskq '%s' destroying\n", | |
1140 | tq_arg.name); | |
1141 | taskq_destroy(tq); | |
1142 | out_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 | ||
1150 | static int | |
1151 | splat_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 | */ | |
1164 | static void | |
1165 | splat_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 | ||
1176 | static int | |
1177 | splat_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); | |
1243 | out: | |
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 | */ | |
1252 | static void | |
1253 | splat_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 | ||
1266 | static int | |
1267 | splat_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); | |
1415 | out: | |
1416 | taskq_destroy(tq); | |
1417 | out_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 | 1425 | splat_subsystem_t * |
1426 | splat_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 | ||
1466 | void | |
7c50328b | 1467 | splat_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 | ||
1484 | int | |
7c50328b | 1485 | splat_taskq_id(void) { |
1486 | return SPLAT_SUBSYSTEM_TASKQ; | |
f1ca4da6 | 1487 | } |