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