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