]> git.proxmox.com Git - mirror_spl-debian.git/blob - module/splat/splat-thread.c
c54cbcd8ac967d6ae441216e281e1ea3229b1b29
[mirror_spl-debian.git] / module / splat / splat-thread.c
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>.
6 * UCRL-CODE-235197
7 *
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://github.com/behlendorf/spl/>.
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.
15 *
16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
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
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting LAyer Tests (SPLAT) Thread Tests.
25 \*****************************************************************************/
26
27 #include <sys/thread.h>
28 #include <sys/random.h>
29 #include "splat-internal.h"
30
31 #define SPLAT_THREAD_NAME "thread"
32 #define SPLAT_THREAD_DESC "Kernel Thread Tests"
33
34 #define SPLAT_THREAD_TEST1_ID 0x0601
35 #define SPLAT_THREAD_TEST1_NAME "create"
36 #define SPLAT_THREAD_TEST1_DESC "Validate thread creation"
37
38 #define SPLAT_THREAD_TEST2_ID 0x0602
39 #define SPLAT_THREAD_TEST2_NAME "exit"
40 #define SPLAT_THREAD_TEST2_DESC "Validate thread exit"
41
42 #define SPLAT_THREAD_TEST3_ID 0x6003
43 #define SPLAT_THREAD_TEST3_NAME "tsd"
44 #define SPLAT_THREAD_TEST3_DESC "Validate thread specific data"
45
46 #define SPLAT_THREAD_TEST_MAGIC 0x4488CC00UL
47 #define SPLAT_THREAD_TEST_KEYS 32
48 #define SPLAT_THREAD_TEST_THREADS 16
49
50 typedef struct thread_priv {
51 unsigned long tp_magic;
52 struct file *tp_file;
53 spinlock_t tp_lock;
54 wait_queue_head_t tp_waitq;
55 uint_t tp_keys[SPLAT_THREAD_TEST_KEYS];
56 int tp_rc;
57 int tp_count;
58 int tp_dtor_count;
59 } thread_priv_t;
60
61 static int
62 splat_thread_rc(thread_priv_t *tp, int rc)
63 {
64 int ret;
65
66 spin_lock(&tp->tp_lock);
67 ret = (tp->tp_rc == rc);
68 spin_unlock(&tp->tp_lock);
69
70 return ret;
71 }
72
73 static int
74 splat_thread_count(thread_priv_t *tp, int count)
75 {
76 int ret;
77
78 spin_lock(&tp->tp_lock);
79 ret = (tp->tp_count == count);
80 spin_unlock(&tp->tp_lock);
81
82 return ret;
83 }
84
85 static void
86 splat_thread_work1(void *priv)
87 {
88 thread_priv_t *tp = (thread_priv_t *)priv;
89
90 spin_lock(&tp->tp_lock);
91 ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
92 tp->tp_rc = 1;
93 wake_up(&tp->tp_waitq);
94 spin_unlock(&tp->tp_lock);
95
96 thread_exit();
97 }
98
99 static int
100 splat_thread_test1(struct file *file, void *arg)
101 {
102 thread_priv_t tp;
103 kthread_t *thr;
104
105 tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
106 tp.tp_file = file;
107 spin_lock_init(&tp.tp_lock);
108 init_waitqueue_head(&tp.tp_waitq);
109 tp.tp_rc = 0;
110
111 thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work1, &tp, 0,
112 &p0, TS_RUN, minclsyspri);
113 /* Must never fail under Solaris, but we check anyway since this
114 * can happen in the linux SPL, we may want to change this behavior */
115 if (thr == NULL)
116 return -ESRCH;
117
118 /* Sleep until the thread sets tp.tp_rc == 1 */
119 wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1));
120
121 splat_vprint(file, SPLAT_THREAD_TEST1_NAME, "%s",
122 "Thread successfully started properly\n");
123 return 0;
124 }
125
126 static void
127 splat_thread_work2(void *priv)
128 {
129 thread_priv_t *tp = (thread_priv_t *)priv;
130
131 spin_lock(&tp->tp_lock);
132 ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
133 tp->tp_rc = 1;
134 wake_up(&tp->tp_waitq);
135 spin_unlock(&tp->tp_lock);
136
137 thread_exit();
138
139 /* The following code is unreachable when thread_exit() is
140 * working properly, which is exactly what we're testing */
141 spin_lock(&tp->tp_lock);
142 tp->tp_rc = 2;
143 wake_up(&tp->tp_waitq);
144 spin_unlock(&tp->tp_lock);
145 }
146
147 static int
148 splat_thread_test2(struct file *file, void *arg)
149 {
150 thread_priv_t tp;
151 kthread_t *thr;
152 int rc = 0;
153
154 tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
155 tp.tp_file = file;
156 spin_lock_init(&tp.tp_lock);
157 init_waitqueue_head(&tp.tp_waitq);
158 tp.tp_rc = 0;
159
160 thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work2, &tp, 0,
161 &p0, TS_RUN, minclsyspri);
162 /* Must never fail under Solaris, but we check anyway since this
163 * can happen in the linux SPL, we may want to change this behavior */
164 if (thr == NULL)
165 return -ESRCH;
166
167 /* Sleep until the thread sets tp.tp_rc == 1 */
168 wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1));
169
170 /* Sleep until the thread sets tp.tp_rc == 2, or until we hit
171 * the timeout. If thread exit is working properly we should
172 * hit the timeout and never see to.tp_rc == 2. */
173 rc = wait_event_timeout(tp.tp_waitq, splat_thread_rc(&tp, 2), HZ / 10);
174 if (rc > 0) {
175 rc = -EINVAL;
176 splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s",
177 "Thread did not exit properly at thread_exit()\n");
178 } else {
179 splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s",
180 "Thread successfully exited at thread_exit()\n");
181 }
182
183 return rc;
184 }
185
186 static void
187 splat_thread_work3_common(thread_priv_t *tp)
188 {
189 ulong_t rnd;
190 int i, rc = 0;
191
192 /* set a unique value for each key using a random value */
193 get_random_bytes((void *)&rnd, 4);
194 for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++)
195 tsd_set(tp->tp_keys[i], (void *)(i + rnd));
196
197 /* verify the unique value for each key */
198 for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++)
199 if (tsd_get(tp->tp_keys[i]) != (void *)(i + rnd))
200 rc = -EINVAL;
201
202 /* set the value to thread_priv_t for use by the destructor */
203 for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++)
204 tsd_set(tp->tp_keys[i], (void *)tp);
205
206 spin_lock(&tp->tp_lock);
207 if (rc && !tp->tp_rc)
208 tp->tp_rc = rc;
209
210 tp->tp_count++;
211 wake_up_all(&tp->tp_waitq);
212 spin_unlock(&tp->tp_lock);
213 }
214
215 static void
216 splat_thread_work3_wait(void *priv)
217 {
218 thread_priv_t *tp = (thread_priv_t *)priv;
219
220 ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
221 splat_thread_work3_common(tp);
222 wait_event(tp->tp_waitq, splat_thread_count(tp, 0));
223 thread_exit();
224 }
225
226 static void
227 splat_thread_work3_exit(void *priv)
228 {
229 thread_priv_t *tp = (thread_priv_t *)priv;
230
231 ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
232 splat_thread_work3_common(tp);
233 thread_exit();
234 }
235
236 static void
237 splat_thread_dtor3(void *priv)
238 {
239 thread_priv_t *tp = (thread_priv_t *)priv;
240
241 ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
242 spin_lock(&tp->tp_lock);
243 tp->tp_dtor_count++;
244 spin_unlock(&tp->tp_lock);
245 }
246
247 /*
248 * Create threads which set and verify SPLAT_THREAD_TEST_KEYS number of
249 * keys. These threads may then exit by calling thread_exit() which calls
250 * tsd_exit() resulting in all their thread specific data being reclaimed.
251 * Alternately, the thread may block in which case the thread specific
252 * data will be reclaimed as part of tsd_destroy(). In either case all
253 * thread specific data must be reclaimed, this is verified by ensuring
254 * the registered destructor is called the correct number of times.
255 */
256 static int
257 splat_thread_test3(struct file *file, void *arg)
258 {
259 int i, rc = 0, expected, wait_count = 0, exit_count = 0;
260 thread_priv_t tp;
261
262 tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
263 tp.tp_file = file;
264 spin_lock_init(&tp.tp_lock);
265 init_waitqueue_head(&tp.tp_waitq);
266 tp.tp_rc = 0;
267 tp.tp_count = 0;
268 tp.tp_dtor_count = 0;
269
270 for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++) {
271 tp.tp_keys[i] = 0;
272 tsd_create(&tp.tp_keys[i], splat_thread_dtor3);
273 }
274
275 /* Start tsd wait threads */
276 for (i = 0; i < SPLAT_THREAD_TEST_THREADS; i++) {
277 if (thread_create(NULL, 0, splat_thread_work3_wait,
278 &tp, 0, &p0, TS_RUN, minclsyspri))
279 wait_count++;
280 }
281
282 /* All wait threads have setup their tsd and are blocking. */
283 wait_event(tp.tp_waitq, splat_thread_count(&tp, wait_count));
284
285 if (tp.tp_dtor_count != 0) {
286 splat_vprint(file, SPLAT_THREAD_TEST3_NAME,
287 "Prematurely ran %d tsd destructors\n", tp.tp_dtor_count);
288 if (!rc)
289 rc = -ERANGE;
290 }
291
292 /* Start tsd exit threads */
293 for (i = 0; i < SPLAT_THREAD_TEST_THREADS; i++) {
294 if (thread_create(NULL, 0, splat_thread_work3_exit,
295 &tp, 0, &p0, TS_RUN, minclsyspri))
296 exit_count++;
297 }
298
299 /* All exit threads verified tsd and are in the process of exiting */
300 wait_event(tp.tp_waitq,splat_thread_count(&tp, wait_count+exit_count));
301 msleep(500);
302
303 expected = (SPLAT_THREAD_TEST_KEYS * exit_count);
304 if (tp.tp_dtor_count != expected) {
305 splat_vprint(file, SPLAT_THREAD_TEST3_NAME,
306 "Expected %d exit tsd destructors but saw %d\n",
307 expected, tp.tp_dtor_count);
308 if (!rc)
309 rc = -ERANGE;
310 }
311
312 /* Destroy all keys and associated tsd in blocked threads */
313 for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++)
314 tsd_destroy(&tp.tp_keys[i]);
315
316 expected = (SPLAT_THREAD_TEST_KEYS * (exit_count + wait_count));
317 if (tp.tp_dtor_count != expected) {
318 splat_vprint(file, SPLAT_THREAD_TEST3_NAME,
319 "Expected %d wait+exit tsd destructors but saw %d\n",
320 expected, tp.tp_dtor_count);
321 if (!rc)
322 rc = -ERANGE;
323 }
324
325 /* Release the remaining wait threads, sleep briefly while they exit */
326 spin_lock(&tp.tp_lock);
327 tp.tp_count = 0;
328 wake_up_all(&tp.tp_waitq);
329 spin_unlock(&tp.tp_lock);
330 msleep(500);
331
332 if (tp.tp_rc) {
333 splat_vprint(file, SPLAT_THREAD_TEST3_NAME,
334 "Thread tsd_get()/tsd_set() error %d\n", tp.tp_rc);
335 if (!rc)
336 rc = tp.tp_rc;
337 } else if (!rc) {
338 splat_vprint(file, SPLAT_THREAD_TEST3_NAME, "%s",
339 "Thread specific data verified\n");
340 }
341
342 return rc;
343 }
344
345 splat_subsystem_t *
346 splat_thread_init(void)
347 {
348 splat_subsystem_t *sub;
349
350 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
351 if (sub == NULL)
352 return NULL;
353
354 memset(sub, 0, sizeof(*sub));
355 strncpy(sub->desc.name, SPLAT_THREAD_NAME, SPLAT_NAME_SIZE);
356 strncpy(sub->desc.desc, SPLAT_THREAD_DESC, SPLAT_DESC_SIZE);
357 INIT_LIST_HEAD(&sub->subsystem_list);
358 INIT_LIST_HEAD(&sub->test_list);
359 spin_lock_init(&sub->test_lock);
360 sub->desc.id = SPLAT_SUBSYSTEM_THREAD;
361
362 SPLAT_TEST_INIT(sub, SPLAT_THREAD_TEST1_NAME, SPLAT_THREAD_TEST1_DESC,
363 SPLAT_THREAD_TEST1_ID, splat_thread_test1);
364 SPLAT_TEST_INIT(sub, SPLAT_THREAD_TEST2_NAME, SPLAT_THREAD_TEST2_DESC,
365 SPLAT_THREAD_TEST2_ID, splat_thread_test2);
366 SPLAT_TEST_INIT(sub, SPLAT_THREAD_TEST3_NAME, SPLAT_THREAD_TEST3_DESC,
367 SPLAT_THREAD_TEST3_ID, splat_thread_test3);
368
369 return sub;
370 }
371
372 void
373 splat_thread_fini(splat_subsystem_t *sub)
374 {
375 ASSERT(sub);
376 SPLAT_TEST_FINI(sub, SPLAT_THREAD_TEST3_ID);
377 SPLAT_TEST_FINI(sub, SPLAT_THREAD_TEST2_ID);
378 SPLAT_TEST_FINI(sub, SPLAT_THREAD_TEST1_ID);
379
380 kfree(sub);
381 }
382
383 int
384 splat_thread_id(void) {
385 return SPLAT_SUBSYSTEM_THREAD;
386 }