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://zfsonlinux.org/>.
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 <linux/slab.h>
30 #include "splat-internal.h"
32 #define SPLAT_THREAD_NAME "thread"
33 #define SPLAT_THREAD_DESC "Kernel Thread Tests"
35 #define SPLAT_THREAD_TEST1_ID 0x0601
36 #define SPLAT_THREAD_TEST1_NAME "create"
37 #define SPLAT_THREAD_TEST1_DESC "Validate thread creation"
39 #define SPLAT_THREAD_TEST2_ID 0x0602
40 #define SPLAT_THREAD_TEST2_NAME "exit"
41 #define SPLAT_THREAD_TEST2_DESC "Validate thread exit"
43 #define SPLAT_THREAD_TEST3_ID 0x6003
44 #define SPLAT_THREAD_TEST3_NAME "tsd"
45 #define SPLAT_THREAD_TEST3_DESC "Validate thread specific data"
47 #define SPLAT_THREAD_TEST_MAGIC 0x4488CC00UL
48 #define SPLAT_THREAD_TEST_KEYS 32
49 #define SPLAT_THREAD_TEST_THREADS 16
51 typedef struct thread_priv
{
52 unsigned long tp_magic
;
55 wait_queue_head_t tp_waitq
;
56 uint_t tp_keys
[SPLAT_THREAD_TEST_KEYS
];
63 splat_thread_rc(thread_priv_t
*tp
, int rc
)
67 spin_lock(&tp
->tp_lock
);
68 ret
= (tp
->tp_rc
== rc
);
69 spin_unlock(&tp
->tp_lock
);
75 splat_thread_count(thread_priv_t
*tp
, int count
)
79 spin_lock(&tp
->tp_lock
);
80 ret
= (tp
->tp_count
== count
);
81 spin_unlock(&tp
->tp_lock
);
87 splat_thread_work1(void *priv
)
89 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
91 spin_lock(&tp
->tp_lock
);
92 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
94 wake_up(&tp
->tp_waitq
);
95 spin_unlock(&tp
->tp_lock
);
101 splat_thread_test1(struct file
*file
, void *arg
)
106 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
108 spin_lock_init(&tp
.tp_lock
);
109 init_waitqueue_head(&tp
.tp_waitq
);
112 thr
= (kthread_t
*)thread_create(NULL
, 0, splat_thread_work1
, &tp
, 0,
113 &p0
, TS_RUN
, minclsyspri
);
114 /* Must never fail under Solaris, but we check anyway since this
115 * can happen in the linux SPL, we may want to change this behavior */
119 /* Sleep until the thread sets tp.tp_rc == 1 */
120 wait_event(tp
.tp_waitq
, splat_thread_rc(&tp
, 1));
122 splat_vprint(file
, SPLAT_THREAD_TEST1_NAME
, "%s",
123 "Thread successfully started properly\n");
128 splat_thread_work2(void *priv
)
130 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
132 spin_lock(&tp
->tp_lock
);
133 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
135 wake_up(&tp
->tp_waitq
);
136 spin_unlock(&tp
->tp_lock
);
140 /* The following code is unreachable when thread_exit() is
141 * working properly, which is exactly what we're testing */
142 spin_lock(&tp
->tp_lock
);
144 wake_up(&tp
->tp_waitq
);
145 spin_unlock(&tp
->tp_lock
);
149 splat_thread_test2(struct file
*file
, void *arg
)
155 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
157 spin_lock_init(&tp
.tp_lock
);
158 init_waitqueue_head(&tp
.tp_waitq
);
161 thr
= (kthread_t
*)thread_create(NULL
, 0, splat_thread_work2
, &tp
, 0,
162 &p0
, TS_RUN
, minclsyspri
);
163 /* Must never fail under Solaris, but we check anyway since this
164 * can happen in the linux SPL, we may want to change this behavior */
168 /* Sleep until the thread sets tp.tp_rc == 1 */
169 wait_event(tp
.tp_waitq
, splat_thread_rc(&tp
, 1));
171 /* Sleep until the thread sets tp.tp_rc == 2, or until we hit
172 * the timeout. If thread exit is working properly we should
173 * hit the timeout and never see to.tp_rc == 2. */
174 rc
= wait_event_timeout(tp
.tp_waitq
, splat_thread_rc(&tp
, 2), HZ
/ 10);
177 splat_vprint(file
, SPLAT_THREAD_TEST2_NAME
, "%s",
178 "Thread did not exit properly at thread_exit()\n");
180 splat_vprint(file
, SPLAT_THREAD_TEST2_NAME
, "%s",
181 "Thread successfully exited at thread_exit()\n");
188 splat_thread_work3_common(thread_priv_t
*tp
)
193 /* set a unique value for each key using a random value */
194 get_random_bytes((void *)&rnd
, 4);
195 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++)
196 tsd_set(tp
->tp_keys
[i
], (void *)(i
+ rnd
));
198 /* verify the unique value for each key */
199 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++)
200 if (tsd_get(tp
->tp_keys
[i
]) != (void *)(i
+ rnd
))
203 /* set the value to thread_priv_t for use by the destructor */
204 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++)
205 tsd_set(tp
->tp_keys
[i
], (void *)tp
);
207 spin_lock(&tp
->tp_lock
);
208 if (rc
&& !tp
->tp_rc
)
212 wake_up_all(&tp
->tp_waitq
);
213 spin_unlock(&tp
->tp_lock
);
217 splat_thread_work3_wait(void *priv
)
219 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
221 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
222 splat_thread_work3_common(tp
);
223 wait_event(tp
->tp_waitq
, splat_thread_count(tp
, 0));
228 splat_thread_work3_exit(void *priv
)
230 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
232 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
233 splat_thread_work3_common(tp
);
238 splat_thread_dtor3(void *priv
)
240 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
242 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
243 spin_lock(&tp
->tp_lock
);
245 spin_unlock(&tp
->tp_lock
);
249 * Create threads which set and verify SPLAT_THREAD_TEST_KEYS number of
250 * keys. These threads may then exit by calling thread_exit() which calls
251 * tsd_exit() resulting in all their thread specific data being reclaimed.
252 * Alternately, the thread may block in which case the thread specific
253 * data will be reclaimed as part of tsd_destroy(). In either case all
254 * thread specific data must be reclaimed, this is verified by ensuring
255 * the registered destructor is called the correct number of times.
258 splat_thread_test3(struct file
*file
, void *arg
)
260 int i
, rc
= 0, expected
, wait_count
= 0, exit_count
= 0;
263 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
265 spin_lock_init(&tp
.tp_lock
);
266 init_waitqueue_head(&tp
.tp_waitq
);
269 tp
.tp_dtor_count
= 0;
271 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++) {
273 tsd_create(&tp
.tp_keys
[i
], splat_thread_dtor3
);
276 /* Start tsd wait threads */
277 for (i
= 0; i
< SPLAT_THREAD_TEST_THREADS
; i
++) {
278 if (thread_create(NULL
, 0, splat_thread_work3_wait
,
279 &tp
, 0, &p0
, TS_RUN
, minclsyspri
))
283 /* All wait threads have setup their tsd and are blocking. */
284 wait_event(tp
.tp_waitq
, splat_thread_count(&tp
, wait_count
));
286 if (tp
.tp_dtor_count
!= 0) {
287 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
288 "Prematurely ran %d tsd destructors\n", tp
.tp_dtor_count
);
293 /* Start tsd exit threads */
294 for (i
= 0; i
< SPLAT_THREAD_TEST_THREADS
; i
++) {
295 if (thread_create(NULL
, 0, splat_thread_work3_exit
,
296 &tp
, 0, &p0
, TS_RUN
, minclsyspri
))
300 /* All exit threads verified tsd and are in the process of exiting */
301 wait_event(tp
.tp_waitq
,splat_thread_count(&tp
, wait_count
+exit_count
));
304 expected
= (SPLAT_THREAD_TEST_KEYS
* exit_count
);
305 if (tp
.tp_dtor_count
!= expected
) {
306 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
307 "Expected %d exit tsd destructors but saw %d\n",
308 expected
, tp
.tp_dtor_count
);
313 /* Destroy all keys and associated tsd in blocked threads */
314 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++)
315 tsd_destroy(&tp
.tp_keys
[i
]);
317 expected
= (SPLAT_THREAD_TEST_KEYS
* (exit_count
+ wait_count
));
318 if (tp
.tp_dtor_count
!= expected
) {
319 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
320 "Expected %d wait+exit tsd destructors but saw %d\n",
321 expected
, tp
.tp_dtor_count
);
326 /* Release the remaining wait threads, sleep briefly while they exit */
327 spin_lock(&tp
.tp_lock
);
329 wake_up_all(&tp
.tp_waitq
);
330 spin_unlock(&tp
.tp_lock
);
334 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
335 "Thread tsd_get()/tsd_set() error %d\n", tp
.tp_rc
);
339 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
, "%s",
340 "Thread specific data verified\n");
347 splat_thread_init(void)
349 splat_subsystem_t
*sub
;
351 sub
= kmalloc(sizeof(*sub
), GFP_KERNEL
);
355 memset(sub
, 0, sizeof(*sub
));
356 strncpy(sub
->desc
.name
, SPLAT_THREAD_NAME
, SPLAT_NAME_SIZE
);
357 strncpy(sub
->desc
.desc
, SPLAT_THREAD_DESC
, SPLAT_DESC_SIZE
);
358 INIT_LIST_HEAD(&sub
->subsystem_list
);
359 INIT_LIST_HEAD(&sub
->test_list
);
360 spin_lock_init(&sub
->test_lock
);
361 sub
->desc
.id
= SPLAT_SUBSYSTEM_THREAD
;
363 SPLAT_TEST_INIT(sub
, SPLAT_THREAD_TEST1_NAME
, SPLAT_THREAD_TEST1_DESC
,
364 SPLAT_THREAD_TEST1_ID
, splat_thread_test1
);
365 SPLAT_TEST_INIT(sub
, SPLAT_THREAD_TEST2_NAME
, SPLAT_THREAD_TEST2_DESC
,
366 SPLAT_THREAD_TEST2_ID
, splat_thread_test2
);
367 SPLAT_TEST_INIT(sub
, SPLAT_THREAD_TEST3_NAME
, SPLAT_THREAD_TEST3_DESC
,
368 SPLAT_THREAD_TEST3_ID
, splat_thread_test3
);
374 splat_thread_fini(splat_subsystem_t
*sub
)
377 SPLAT_TEST_FINI(sub
, SPLAT_THREAD_TEST3_ID
);
378 SPLAT_TEST_FINI(sub
, SPLAT_THREAD_TEST2_ID
);
379 SPLAT_TEST_FINI(sub
, SPLAT_THREAD_TEST1_ID
);
385 splat_thread_id(void) {
386 return SPLAT_SUBSYSTEM_THREAD
;