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>.
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://github.com/behlendorf/spl/>.
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.
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
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 \*****************************************************************************/
27 #include <sys/thread.h>
28 #include <sys/random.h>
29 #include "splat-internal.h"
31 #define SPLAT_THREAD_NAME "thread"
32 #define SPLAT_THREAD_DESC "Kernel Thread Tests"
34 #define SPLAT_THREAD_TEST1_ID 0x0601
35 #define SPLAT_THREAD_TEST1_NAME "create"
36 #define SPLAT_THREAD_TEST1_DESC "Validate thread creation"
38 #define SPLAT_THREAD_TEST2_ID 0x0602
39 #define SPLAT_THREAD_TEST2_NAME "exit"
40 #define SPLAT_THREAD_TEST2_DESC "Validate thread exit"
42 #define SPLAT_THREAD_TEST3_ID 0x6003
43 #define SPLAT_THREAD_TEST3_NAME "tsd"
44 #define SPLAT_THREAD_TEST3_DESC "Validate thread specific data"
46 #define SPLAT_THREAD_TEST_MAGIC 0x4488CC00UL
47 #define SPLAT_THREAD_TEST_KEYS 32
48 #define SPLAT_THREAD_TEST_THREADS 16
50 typedef struct thread_priv
{
51 unsigned long tp_magic
;
54 wait_queue_head_t tp_waitq
;
55 uint_t tp_keys
[SPLAT_THREAD_TEST_KEYS
];
62 splat_thread_rc(thread_priv_t
*tp
, int rc
)
66 spin_lock(&tp
->tp_lock
);
67 ret
= (tp
->tp_rc
== rc
);
68 spin_unlock(&tp
->tp_lock
);
74 splat_thread_count(thread_priv_t
*tp
, int count
)
78 spin_lock(&tp
->tp_lock
);
79 ret
= (tp
->tp_count
== count
);
80 spin_unlock(&tp
->tp_lock
);
86 splat_thread_work1(void *priv
)
88 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
90 spin_lock(&tp
->tp_lock
);
91 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
93 wake_up(&tp
->tp_waitq
);
94 spin_unlock(&tp
->tp_lock
);
100 splat_thread_test1(struct file
*file
, void *arg
)
105 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
107 spin_lock_init(&tp
.tp_lock
);
108 init_waitqueue_head(&tp
.tp_waitq
);
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 */
118 /* Sleep until the thread sets tp.tp_rc == 1 */
119 wait_event(tp
.tp_waitq
, splat_thread_rc(&tp
, 1));
121 splat_vprint(file
, SPLAT_THREAD_TEST1_NAME
, "%s",
122 "Thread successfully started properly\n");
127 splat_thread_work2(void *priv
)
129 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
131 spin_lock(&tp
->tp_lock
);
132 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
134 wake_up(&tp
->tp_waitq
);
135 spin_unlock(&tp
->tp_lock
);
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
);
143 wake_up(&tp
->tp_waitq
);
144 spin_unlock(&tp
->tp_lock
);
148 splat_thread_test2(struct file
*file
, void *arg
)
154 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
156 spin_lock_init(&tp
.tp_lock
);
157 init_waitqueue_head(&tp
.tp_waitq
);
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 */
167 /* Sleep until the thread sets tp.tp_rc == 1 */
168 wait_event(tp
.tp_waitq
, splat_thread_rc(&tp
, 1));
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);
176 splat_vprint(file
, SPLAT_THREAD_TEST2_NAME
, "%s",
177 "Thread did not exit properly at thread_exit()\n");
179 splat_vprint(file
, SPLAT_THREAD_TEST2_NAME
, "%s",
180 "Thread successfully exited at thread_exit()\n");
187 splat_thread_work3_common(thread_priv_t
*tp
)
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
));
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
))
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
);
206 spin_lock(&tp
->tp_lock
);
207 if (rc
&& !tp
->tp_rc
)
211 wake_up_all(&tp
->tp_waitq
);
212 spin_unlock(&tp
->tp_lock
);
216 splat_thread_work3_wait(void *priv
)
218 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
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));
227 splat_thread_work3_exit(void *priv
)
229 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
231 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
232 splat_thread_work3_common(tp
);
237 splat_thread_dtor3(void *priv
)
239 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
241 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
242 spin_lock(&tp
->tp_lock
);
244 spin_unlock(&tp
->tp_lock
);
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.
257 splat_thread_test3(struct file
*file
, void *arg
)
259 int i
, rc
= 0, expected
, wait_count
= 0, exit_count
= 0;
262 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
264 spin_lock_init(&tp
.tp_lock
);
265 init_waitqueue_head(&tp
.tp_waitq
);
268 tp
.tp_dtor_count
= 0;
270 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++) {
272 tsd_create(&tp
.tp_keys
[i
], splat_thread_dtor3
);
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
))
282 /* All wait threads have setup their tsd and are blocking. */
283 wait_event(tp
.tp_waitq
, splat_thread_count(&tp
, wait_count
));
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
);
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
))
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
));
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
);
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
]);
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
);
325 /* Release the remaining wait threads, sleep briefly while they exit */
326 spin_lock(&tp
.tp_lock
);
328 wake_up_all(&tp
.tp_waitq
);
329 spin_unlock(&tp
.tp_lock
);
333 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
334 "Thread tsd_get()/tsd_set() error %d\n", tp
.tp_rc
);
338 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
, "%s",
339 "Thread specific data verified\n");
346 splat_thread_init(void)
348 splat_subsystem_t
*sub
;
350 sub
= kmalloc(sizeof(*sub
), GFP_KERNEL
);
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
;
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
);
373 splat_thread_fini(splat_subsystem_t
*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
);
384 splat_thread_id(void) {
385 return SPLAT_SUBSYSTEM_THREAD
;