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/delay.h>
30 #include <linux/mm_compat.h>
31 #include <linux/wait_compat.h>
32 #include <linux/slab.h>
33 #include "splat-internal.h"
35 #define SPLAT_THREAD_NAME "thread"
36 #define SPLAT_THREAD_DESC "Kernel Thread Tests"
38 #define SPLAT_THREAD_TEST1_ID 0x0601
39 #define SPLAT_THREAD_TEST1_NAME "create"
40 #define SPLAT_THREAD_TEST1_DESC "Validate thread creation"
42 #define SPLAT_THREAD_TEST2_ID 0x0602
43 #define SPLAT_THREAD_TEST2_NAME "exit"
44 #define SPLAT_THREAD_TEST2_DESC "Validate thread exit"
46 #define SPLAT_THREAD_TEST3_ID 0x6003
47 #define SPLAT_THREAD_TEST3_NAME "tsd"
48 #define SPLAT_THREAD_TEST3_DESC "Validate thread specific data"
50 #define SPLAT_THREAD_TEST_MAGIC 0x4488CC00UL
51 #define SPLAT_THREAD_TEST_KEYS 32
52 #define SPLAT_THREAD_TEST_THREADS 16
54 typedef struct thread_priv
{
55 unsigned long tp_magic
;
58 spl_wait_queue_head_t tp_waitq
;
59 uint_t tp_keys
[SPLAT_THREAD_TEST_KEYS
];
66 splat_thread_rc(thread_priv_t
*tp
, int rc
)
70 spin_lock(&tp
->tp_lock
);
71 ret
= (tp
->tp_rc
== rc
);
72 spin_unlock(&tp
->tp_lock
);
78 splat_thread_count(thread_priv_t
*tp
, int count
)
82 spin_lock(&tp
->tp_lock
);
83 ret
= (tp
->tp_count
== count
);
84 spin_unlock(&tp
->tp_lock
);
90 splat_thread_work1(void *priv
)
92 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
94 spin_lock(&tp
->tp_lock
);
95 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
97 wake_up(&tp
->tp_waitq
);
98 spin_unlock(&tp
->tp_lock
);
104 splat_thread_test1(struct file
*file
, void *arg
)
109 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
111 spin_lock_init(&tp
.tp_lock
);
112 init_waitqueue_head(&tp
.tp_waitq
);
115 thr
= (kthread_t
*)thread_create(NULL
, 0, splat_thread_work1
, &tp
, 0,
116 &p0
, TS_RUN
, defclsyspri
);
117 /* Must never fail under Solaris, but we check anyway since this
118 * can happen in the linux SPL, we may want to change this behavior */
122 /* Sleep until the thread sets tp.tp_rc == 1 */
123 wait_event(tp
.tp_waitq
, splat_thread_rc(&tp
, 1));
125 splat_vprint(file
, SPLAT_THREAD_TEST1_NAME
, "%s",
126 "Thread successfully started properly\n");
131 splat_thread_work2(void *priv
)
133 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
135 spin_lock(&tp
->tp_lock
);
136 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
138 wake_up(&tp
->tp_waitq
);
139 spin_unlock(&tp
->tp_lock
);
143 /* The following code is unreachable when thread_exit() is
144 * working properly, which is exactly what we're testing */
145 spin_lock(&tp
->tp_lock
);
147 wake_up(&tp
->tp_waitq
);
148 spin_unlock(&tp
->tp_lock
);
152 splat_thread_test2(struct file
*file
, void *arg
)
158 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
160 spin_lock_init(&tp
.tp_lock
);
161 init_waitqueue_head(&tp
.tp_waitq
);
164 thr
= (kthread_t
*)thread_create(NULL
, 0, splat_thread_work2
, &tp
, 0,
165 &p0
, TS_RUN
, defclsyspri
);
166 /* Must never fail under Solaris, but we check anyway since this
167 * can happen in the linux SPL, we may want to change this behavior */
171 /* Sleep until the thread sets tp.tp_rc == 1 */
172 wait_event(tp
.tp_waitq
, splat_thread_rc(&tp
, 1));
174 /* Sleep until the thread sets tp.tp_rc == 2, or until we hit
175 * the timeout. If thread exit is working properly we should
176 * hit the timeout and never see to.tp_rc == 2. */
177 rc
= wait_event_timeout(tp
.tp_waitq
, splat_thread_rc(&tp
, 2), HZ
/ 10);
180 splat_vprint(file
, SPLAT_THREAD_TEST2_NAME
, "%s",
181 "Thread did not exit properly at thread_exit()\n");
183 splat_vprint(file
, SPLAT_THREAD_TEST2_NAME
, "%s",
184 "Thread successfully exited at thread_exit()\n");
191 splat_thread_work3_common(thread_priv_t
*tp
)
196 /* set a unique value for each key using a random value */
197 get_random_bytes((void *)&rnd
, 4);
198 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++)
199 tsd_set(tp
->tp_keys
[i
], (void *)(i
+ rnd
));
201 /* verify the unique value for each key */
202 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++)
203 if (tsd_get(tp
->tp_keys
[i
]) != (void *)(i
+ rnd
))
206 /* set the value to thread_priv_t for use by the destructor */
207 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++)
208 tsd_set(tp
->tp_keys
[i
], (void *)tp
);
210 spin_lock(&tp
->tp_lock
);
211 if (rc
&& !tp
->tp_rc
)
215 wake_up_all(&tp
->tp_waitq
);
216 spin_unlock(&tp
->tp_lock
);
220 splat_thread_work3_wait(void *priv
)
222 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
224 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
225 splat_thread_work3_common(tp
);
226 wait_event(tp
->tp_waitq
, splat_thread_count(tp
, 0));
231 splat_thread_work3_exit(void *priv
)
233 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
235 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
236 splat_thread_work3_common(tp
);
241 splat_thread_dtor3(void *priv
)
243 thread_priv_t
*tp
= (thread_priv_t
*)priv
;
245 ASSERT(tp
->tp_magic
== SPLAT_THREAD_TEST_MAGIC
);
246 spin_lock(&tp
->tp_lock
);
248 spin_unlock(&tp
->tp_lock
);
252 * Create threads which set and verify SPLAT_THREAD_TEST_KEYS number of
253 * keys. These threads may then exit by calling thread_exit() which calls
254 * tsd_exit() resulting in all their thread specific data being reclaimed.
255 * Alternately, the thread may block in which case the thread specific
256 * data will be reclaimed as part of tsd_destroy(). In either case all
257 * thread specific data must be reclaimed, this is verified by ensuring
258 * the registered destructor is called the correct number of times.
261 splat_thread_test3(struct file
*file
, void *arg
)
263 int i
, rc
= 0, expected
, wait_count
= 0, exit_count
= 0;
266 tp
.tp_magic
= SPLAT_THREAD_TEST_MAGIC
;
268 spin_lock_init(&tp
.tp_lock
);
269 init_waitqueue_head(&tp
.tp_waitq
);
272 tp
.tp_dtor_count
= 0;
274 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++) {
276 tsd_create(&tp
.tp_keys
[i
], splat_thread_dtor3
);
279 /* Start tsd wait threads */
280 for (i
= 0; i
< SPLAT_THREAD_TEST_THREADS
; i
++) {
281 if (thread_create(NULL
, 0, splat_thread_work3_wait
,
282 &tp
, 0, &p0
, TS_RUN
, defclsyspri
))
286 /* All wait threads have setup their tsd and are blocking. */
287 wait_event(tp
.tp_waitq
, splat_thread_count(&tp
, wait_count
));
289 if (tp
.tp_dtor_count
!= 0) {
290 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
291 "Prematurely ran %d tsd destructors\n", tp
.tp_dtor_count
);
296 /* Start tsd exit threads */
297 for (i
= 0; i
< SPLAT_THREAD_TEST_THREADS
; i
++) {
298 if (thread_create(NULL
, 0, splat_thread_work3_exit
,
299 &tp
, 0, &p0
, TS_RUN
, defclsyspri
))
303 /* All exit threads verified tsd and are in the process of exiting */
304 wait_event(tp
.tp_waitq
,splat_thread_count(&tp
, wait_count
+exit_count
));
307 expected
= (SPLAT_THREAD_TEST_KEYS
* exit_count
);
308 if (tp
.tp_dtor_count
!= expected
) {
309 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
310 "Expected %d exit tsd destructors but saw %d\n",
311 expected
, tp
.tp_dtor_count
);
316 /* Destroy all keys and associated tsd in blocked threads */
317 for (i
= 0; i
< SPLAT_THREAD_TEST_KEYS
; i
++)
318 tsd_destroy(&tp
.tp_keys
[i
]);
320 expected
= (SPLAT_THREAD_TEST_KEYS
* (exit_count
+ wait_count
));
321 if (tp
.tp_dtor_count
!= expected
) {
322 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
323 "Expected %d wait+exit tsd destructors but saw %d\n",
324 expected
, tp
.tp_dtor_count
);
329 /* Release the remaining wait threads, sleep briefly while they exit */
330 spin_lock(&tp
.tp_lock
);
332 wake_up_all(&tp
.tp_waitq
);
333 spin_unlock(&tp
.tp_lock
);
337 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
,
338 "Thread tsd_get()/tsd_set() error %d\n", tp
.tp_rc
);
342 splat_vprint(file
, SPLAT_THREAD_TEST3_NAME
, "%s",
343 "Thread specific data verified\n");
350 splat_thread_init(void)
352 splat_subsystem_t
*sub
;
354 sub
= kmalloc(sizeof(*sub
), GFP_KERNEL
);
358 memset(sub
, 0, sizeof(*sub
));
359 strncpy(sub
->desc
.name
, SPLAT_THREAD_NAME
, SPLAT_NAME_SIZE
);
360 strncpy(sub
->desc
.desc
, SPLAT_THREAD_DESC
, SPLAT_DESC_SIZE
);
361 INIT_LIST_HEAD(&sub
->subsystem_list
);
362 INIT_LIST_HEAD(&sub
->test_list
);
363 spin_lock_init(&sub
->test_lock
);
364 sub
->desc
.id
= SPLAT_SUBSYSTEM_THREAD
;
366 splat_test_init(sub
, SPLAT_THREAD_TEST1_NAME
, SPLAT_THREAD_TEST1_DESC
,
367 SPLAT_THREAD_TEST1_ID
, splat_thread_test1
);
368 splat_test_init(sub
, SPLAT_THREAD_TEST2_NAME
, SPLAT_THREAD_TEST2_DESC
,
369 SPLAT_THREAD_TEST2_ID
, splat_thread_test2
);
370 splat_test_init(sub
, SPLAT_THREAD_TEST3_NAME
, SPLAT_THREAD_TEST3_DESC
,
371 SPLAT_THREAD_TEST3_ID
, splat_thread_test3
);
377 splat_thread_fini(splat_subsystem_t
*sub
)
380 splat_test_fini(sub
, SPLAT_THREAD_TEST3_ID
);
381 splat_test_fini(sub
, SPLAT_THREAD_TEST2_ID
);
382 splat_test_fini(sub
, SPLAT_THREAD_TEST1_ID
);
388 splat_thread_id(void) {
389 return SPLAT_SUBSYSTEM_THREAD
;