]> git.proxmox.com Git - mirror_spl-debian.git/blob - module/splat/splat-taskq.c
Fix taskq_wait() not waiting bug
[mirror_spl-debian.git] / module / splat / splat-taskq.c
1 /*
2 * This file is part of the SPL: Solaris Porting Layer.
3 *
4 * Copyright (c) 2008 Lawrence Livermore National Security, LLC.
5 * Produced at Lawrence Livermore National Laboratory
6 * Written by:
7 * Brian Behlendorf <behlendorf1@llnl.gov>,
8 * Herb Wartens <wartens2@llnl.gov>,
9 * Jim Garlick <garlick@llnl.gov>
10 * UCRL-CODE-235197
11 *
12 * This is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 *
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 */
26
27 #include "splat-internal.h"
28
29 #define SPLAT_SUBSYSTEM_TASKQ 0x0200
30 #define SPLAT_TASKQ_NAME "taskq"
31 #define SPLAT_TASKQ_DESC "Kernel Task Queue Tests"
32
33 #define SPLAT_TASKQ_TEST1_ID 0x0201
34 #define SPLAT_TASKQ_TEST1_NAME "single"
35 #define SPLAT_TASKQ_TEST1_DESC "Single task queue, single task"
36
37 #define SPLAT_TASKQ_TEST2_ID 0x0202
38 #define SPLAT_TASKQ_TEST2_NAME "multiple"
39 #define SPLAT_TASKQ_TEST2_DESC "Multiple task queues, multiple tasks"
40
41 #define SPLAT_TASKQ_TEST3_ID 0x0203
42 #define SPLAT_TASKQ_TEST3_NAME "system"
43 #define SPLAT_TASKQ_TEST3_DESC "System task queue, multiple tasks"
44
45 #define SPLAT_TASKQ_TEST4_ID 0x0204
46 #define SPLAT_TASKQ_TEST4_NAME "wait"
47 #define SPLAT_TASKQ_TEST4_DESC "Multiple task waiting"
48
49 typedef struct splat_taskq_arg {
50 int flag;
51 int id;
52 atomic_t count;
53 struct file *file;
54 const char *name;
55 } splat_taskq_arg_t;
56
57 /* Validation Test 1 - Create a taskq, queue a task, wait until
58 * task completes, ensure task ran properly, cleanup taskq,
59 */
60 static void
61 splat_taskq_test13_func(void *arg)
62 {
63 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
64
65 ASSERT(tq_arg);
66 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST1_NAME,
67 "Taskq '%s' function '%s' setting flag\n",
68 tq_arg->name, sym2str(splat_taskq_test13_func));
69 tq_arg->flag = 1;
70 }
71
72 static int
73 splat_taskq_test1(struct file *file, void *arg)
74 {
75 taskq_t *tq;
76 taskqid_t id;
77 splat_taskq_arg_t tq_arg;
78
79 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' creating\n",
80 SPLAT_TASKQ_TEST1_NAME);
81 if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, maxclsyspri,
82 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
83 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
84 "Taskq '%s' create failed\n",
85 SPLAT_TASKQ_TEST1_NAME);
86 return -EINVAL;
87 }
88
89 tq_arg.flag = 0;
90 tq_arg.id = 0;
91 tq_arg.file = file;
92 tq_arg.name = SPLAT_TASKQ_TEST1_NAME;
93
94 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
95 "Taskq '%s' function '%s' dispatching\n",
96 tq_arg.name, sym2str(splat_taskq_test13_func));
97 if ((id = taskq_dispatch(tq, splat_taskq_test13_func,
98 &tq_arg, TQ_SLEEP)) == 0) {
99 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
100 "Taskq '%s' function '%s' dispatch failed\n",
101 tq_arg.name, sym2str(splat_taskq_test13_func));
102 taskq_destroy(tq);
103 return -EINVAL;
104 }
105
106 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
107 tq_arg.name);
108 taskq_wait(tq);
109 splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
110 tq_arg.name);
111 taskq_destroy(tq);
112
113 return (tq_arg.flag) ? 0 : -EINVAL;
114 }
115
116 /* Validation Test 2 - Create multiple taskq's, each with multiple tasks,
117 * wait until all tasks complete, ensure all tasks ran properly and in the
118 * the correct order, cleanup taskq's
119 */
120 static void
121 splat_taskq_test2_func1(void *arg)
122 {
123 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
124
125 ASSERT(tq_arg);
126 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
127 "Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
128 tq_arg->name, tq_arg->id,
129 sym2str(splat_taskq_test2_func1),
130 tq_arg->flag * 2, tq_arg->flag);
131 tq_arg->flag *= 2;
132 }
133
134 static void
135 splat_taskq_test2_func2(void *arg)
136 {
137 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
138
139 ASSERT(tq_arg);
140 splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
141 "Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
142 tq_arg->name, tq_arg->id,
143 sym2str(splat_taskq_test2_func2),
144 tq_arg->flag + 1, tq_arg->flag);
145 tq_arg->flag += 1;
146 }
147
148 #define TEST2_TASKQS 8
149 #define TEST2_THREADS_PER_TASKQ 4
150
151 static int
152 splat_taskq_test2(struct file *file, void *arg) {
153 taskq_t *tq[TEST2_TASKQS] = { NULL };
154 taskqid_t id;
155 splat_taskq_arg_t tq_args[TEST2_TASKQS];
156 int i, rc = 0;
157
158 for (i = 0; i < TEST2_TASKQS; i++) {
159
160 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq '%s/%d' "
161 "creating\n", SPLAT_TASKQ_TEST2_NAME, i);
162 if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME,
163 TEST2_THREADS_PER_TASKQ,
164 maxclsyspri, 50, INT_MAX,
165 TASKQ_PREPOPULATE)) == NULL) {
166 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
167 "Taskq '%s/%d' create failed\n",
168 SPLAT_TASKQ_TEST2_NAME, i);
169 rc = -EINVAL;
170 break;
171 }
172
173 tq_args[i].flag = i;
174 tq_args[i].id = i;
175 tq_args[i].file = file;
176 tq_args[i].name = SPLAT_TASKQ_TEST2_NAME;
177
178 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
179 "Taskq '%s/%d' function '%s' dispatching\n",
180 tq_args[i].name, tq_args[i].id,
181 sym2str(splat_taskq_test2_func1));
182 if ((id = taskq_dispatch(
183 tq[i], splat_taskq_test2_func1,
184 &tq_args[i], TQ_SLEEP)) == 0) {
185 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
186 "Taskq '%s/%d' function '%s' dispatch "
187 "failed\n", tq_args[i].name, tq_args[i].id,
188 sym2str(splat_taskq_test2_func1));
189 rc = -EINVAL;
190 break;
191 }
192
193 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
194 "Taskq '%s/%d' function '%s' dispatching\n",
195 tq_args[i].name, tq_args[i].id,
196 sym2str(splat_taskq_test2_func2));
197 if ((id = taskq_dispatch(
198 tq[i], splat_taskq_test2_func2,
199 &tq_args[i], TQ_SLEEP)) == 0) {
200 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
201 "Taskq '%s/%d' function '%s' dispatch failed\n",
202 tq_args[i].name, tq_args[i].id,
203 sym2str(splat_taskq_test2_func2));
204 rc = -EINVAL;
205 break;
206 }
207 }
208
209 /* When rc is set we're effectively just doing cleanup here, so
210 * ignore new errors in that case. They just cause noise. */
211 for (i = 0; i < TEST2_TASKQS; i++) {
212 if (tq[i] != NULL) {
213 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
214 "Taskq '%s/%d' waiting\n",
215 tq_args[i].name, tq_args[i].id);
216 taskq_wait(tq[i]);
217 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
218 "Taskq '%s/%d; destroying\n",
219 tq_args[i].name, tq_args[i].id);
220 taskq_destroy(tq[i]);
221
222 if (!rc && tq_args[i].flag != ((i * 2) + 1)) {
223 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
224 "Taskq '%s/%d' processed tasks "
225 "out of order; %d != %d\n",
226 tq_args[i].name, tq_args[i].id,
227 tq_args[i].flag, i * 2 + 1);
228 rc = -EINVAL;
229 } else {
230 splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
231 "Taskq '%s/%d' processed tasks "
232 "in the correct order; %d == %d\n",
233 tq_args[i].name, tq_args[i].id,
234 tq_args[i].flag, i * 2 + 1);
235 }
236 }
237 }
238
239 return rc;
240 }
241
242 /* Validation Test 3 - Use the global system task queue with a single
243 * task, * wait until task completes, ensure task ran properly.
244 */
245 static int
246 splat_taskq_test3(struct file *file, void *arg)
247 {
248 taskqid_t id;
249 splat_taskq_arg_t tq_arg;
250
251 tq_arg.flag = 0;
252 tq_arg.id = 0;
253 tq_arg.file = file;
254 tq_arg.name = SPLAT_TASKQ_TEST3_NAME;
255
256 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
257 "Taskq '%s' function '%s' dispatching\n",
258 tq_arg.name, sym2str(splat_taskq_test13_func));
259 if ((id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
260 &tq_arg, TQ_SLEEP)) == 0) {
261 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
262 "Taskq '%s' function '%s' dispatch failed\n",
263 tq_arg.name, sym2str(splat_taskq_test13_func));
264 return -EINVAL;
265 }
266
267 splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
268 tq_arg.name);
269 taskq_wait(system_taskq);
270
271 return (tq_arg.flag) ? 0 : -EINVAL;
272 }
273
274 static void
275 splat_taskq_test4_func(void *arg)
276 {
277 splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
278 ASSERT(tq_arg);
279
280 atomic_inc(&tq_arg->count);
281 }
282
283 static int
284 splat_taskq_test4(struct file *file, void *arg)
285 {
286 taskq_t *tq;
287 splat_taskq_arg_t tq_arg;
288 int i, j, rc = 0;
289
290 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' creating\n",
291 SPLAT_TASKQ_TEST4_NAME);
292 if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, maxclsyspri,
293 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
294 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
295 "Taskq '%s' create failed\n",
296 SPLAT_TASKQ_TEST4_NAME);
297 return -EINVAL;
298 }
299
300 tq_arg.file = file;
301 tq_arg.name = SPLAT_TASKQ_TEST4_NAME;
302
303 for (i = 1; i <= 1024; i *= 2) {
304 atomic_set(&tq_arg.count, 0);
305 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
306 "Taskq '%s' function '%s' dispatched %d times\n",
307 tq_arg.name, sym2str(splat_taskq_test4_func), i);
308
309 for (j = 0; j < i; j++) {
310 if ((taskq_dispatch(tq, splat_taskq_test4_func,
311 &tq_arg, TQ_SLEEP)) == 0) {
312 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME,
313 "Taskq '%s' function '%s' dispatch "
314 "%d failed\n", tq_arg.name,
315 sym2str(splat_taskq_test13_func), j);
316 rc = -EINVAL;
317 goto out;
318 }
319 }
320
321 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
322 "waiting for %d dispatches\n", tq_arg.name, i);
323 taskq_wait(tq);
324 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' "
325 "%d/%d dispatches finished\n", tq_arg.name,
326 atomic_read(&tq_arg.count), i);
327 if (atomic_read(&tq_arg.count) != i) {
328 rc = -ERANGE;
329 goto out;
330
331 }
332 }
333 out:
334 splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' destroying\n",
335 tq_arg.name);
336 taskq_destroy(tq);
337
338 return rc;
339 }
340
341 splat_subsystem_t *
342 splat_taskq_init(void)
343 {
344 splat_subsystem_t *sub;
345
346 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
347 if (sub == NULL)
348 return NULL;
349
350 memset(sub, 0, sizeof(*sub));
351 strncpy(sub->desc.name, SPLAT_TASKQ_NAME, SPLAT_NAME_SIZE);
352 strncpy(sub->desc.desc, SPLAT_TASKQ_DESC, SPLAT_DESC_SIZE);
353 INIT_LIST_HEAD(&sub->subsystem_list);
354 INIT_LIST_HEAD(&sub->test_list);
355 spin_lock_init(&sub->test_lock);
356 sub->desc.id = SPLAT_SUBSYSTEM_TASKQ;
357
358 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST1_NAME, SPLAT_TASKQ_TEST1_DESC,
359 SPLAT_TASKQ_TEST1_ID, splat_taskq_test1);
360 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST2_NAME, SPLAT_TASKQ_TEST2_DESC,
361 SPLAT_TASKQ_TEST2_ID, splat_taskq_test2);
362 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST3_NAME, SPLAT_TASKQ_TEST3_DESC,
363 SPLAT_TASKQ_TEST3_ID, splat_taskq_test3);
364 SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST4_NAME, SPLAT_TASKQ_TEST4_DESC,
365 SPLAT_TASKQ_TEST4_ID, splat_taskq_test4);
366
367 return sub;
368 }
369
370 void
371 splat_taskq_fini(splat_subsystem_t *sub)
372 {
373 ASSERT(sub);
374 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST4_ID);
375 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST3_ID);
376 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST2_ID);
377 SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST1_ID);
378
379 kfree(sub);
380 }
381
382 int
383 splat_taskq_id(void) {
384 return SPLAT_SUBSYSTEM_TASKQ;
385 }