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) Condition Variable Tests.
25 \*****************************************************************************/
27 #include <sys/condvar.h>
28 #include <sys/timer.h>
29 #include <sys/thread.h>
30 #include "splat-internal.h"
32 #define SPLAT_CONDVAR_NAME "condvar"
33 #define SPLAT_CONDVAR_DESC "Kernel Condition Variable Tests"
35 #define SPLAT_CONDVAR_TEST1_ID 0x0501
36 #define SPLAT_CONDVAR_TEST1_NAME "signal1"
37 #define SPLAT_CONDVAR_TEST1_DESC "Wake a single thread, cv_wait()/cv_signal()"
39 #define SPLAT_CONDVAR_TEST2_ID 0x0502
40 #define SPLAT_CONDVAR_TEST2_NAME "broadcast1"
41 #define SPLAT_CONDVAR_TEST2_DESC "Wake all threads, cv_wait()/cv_broadcast()"
43 #define SPLAT_CONDVAR_TEST3_ID 0x0503
44 #define SPLAT_CONDVAR_TEST3_NAME "signal2"
45 #define SPLAT_CONDVAR_TEST3_DESC "Wake a single thread, cv_wait_timeout()/cv_signal()"
47 #define SPLAT_CONDVAR_TEST4_ID 0x0504
48 #define SPLAT_CONDVAR_TEST4_NAME "broadcast2"
49 #define SPLAT_CONDVAR_TEST4_DESC "Wake all threads, cv_wait_timeout()/cv_broadcast()"
51 #define SPLAT_CONDVAR_TEST5_ID 0x0505
52 #define SPLAT_CONDVAR_TEST5_NAME "timeout"
53 #define SPLAT_CONDVAR_TEST5_DESC "Timeout thread, cv_wait_timeout()"
55 #define SPLAT_CONDVAR_TEST_MAGIC 0x115599DDUL
56 #define SPLAT_CONDVAR_TEST_NAME "condvar"
57 #define SPLAT_CONDVAR_TEST_COUNT 8
59 typedef struct condvar_priv
{
60 unsigned long cv_magic
;
62 kcondvar_t cv_condvar
;
66 typedef struct condvar_thr
{
68 condvar_priv_t
*ct_cvp
;
69 struct task_struct
*ct_thread
;
74 splat_condvar_test12_thread(void *arg
)
76 condvar_thr_t
*ct
= (condvar_thr_t
*)arg
;
77 condvar_priv_t
*cv
= ct
->ct_cvp
;
79 ASSERT(cv
->cv_magic
== SPLAT_CONDVAR_TEST_MAGIC
);
81 mutex_enter(&cv
->cv_mtx
);
82 splat_vprint(cv
->cv_file
, ct
->ct_name
,
83 "%s thread sleeping with %d waiters\n",
84 ct
->ct_thread
->comm
, atomic_read(&cv
->cv_condvar
.cv_waiters
));
85 cv_wait(&cv
->cv_condvar
, &cv
->cv_mtx
);
86 splat_vprint(cv
->cv_file
, ct
->ct_name
,
87 "%s thread woken %d waiters remain\n",
88 ct
->ct_thread
->comm
, atomic_read(&cv
->cv_condvar
.cv_waiters
));
89 mutex_exit(&cv
->cv_mtx
);
91 /* wait for main thread reap us */
92 while (!kthread_should_stop())
98 splat_condvar_test1(struct file
*file
, void *arg
)
100 int i
, count
= 0, rc
= 0;
101 condvar_thr_t ct
[SPLAT_CONDVAR_TEST_COUNT
];
104 cv
.cv_magic
= SPLAT_CONDVAR_TEST_MAGIC
;
106 mutex_init(&cv
.cv_mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
107 cv_init(&cv
.cv_condvar
, NULL
, CV_DEFAULT
, NULL
);
109 /* Create some threads, the exact number isn't important just as
110 * long as we know how many we managed to create and should expect. */
111 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
113 ct
[i
].ct_name
= SPLAT_CONDVAR_TEST1_NAME
;
115 ct
[i
].ct_thread
= spl_kthread_create(splat_condvar_test12_thread
,
116 &ct
[i
], "%s/%d", SPLAT_CONDVAR_TEST_NAME
, i
);
118 if (!IS_ERR(ct
[i
].ct_thread
)) {
119 wake_up_process(ct
[i
].ct_thread
);
124 /* Wait until all threads are waiting on the condition variable */
125 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) != count
)
128 /* Wake a single thread at a time, wait until it exits */
129 for (i
= 1; i
<= count
; i
++) {
130 cv_signal(&cv
.cv_condvar
);
132 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) > (count
- i
))
135 /* Correct behavior 1 thread woken */
136 if (atomic_read(&cv
.cv_condvar
.cv_waiters
) == (count
- i
))
139 splat_vprint(file
, SPLAT_CONDVAR_TEST1_NAME
, "Attempted to "
140 "wake %d thread but work %d threads woke\n",
141 1, count
- atomic_read(&cv
.cv_condvar
.cv_waiters
));
147 splat_vprint(file
, SPLAT_CONDVAR_TEST1_NAME
, "Correctly woke "
148 "%d sleeping threads %d at a time\n", count
, 1);
150 /* Wait until that last nutex is dropped */
151 while (mutex_owner(&cv
.cv_mtx
))
154 /* Wake everything for the failure case */
155 cv_broadcast(&cv
.cv_condvar
);
156 cv_destroy(&cv
.cv_condvar
);
158 /* wait for threads to exit */
159 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
160 if (!IS_ERR(ct
[i
].ct_thread
))
161 kthread_stop(ct
[i
].ct_thread
);
163 mutex_destroy(&cv
.cv_mtx
);
169 splat_condvar_test2(struct file
*file
, void *arg
)
171 int i
, count
= 0, rc
= 0;
172 condvar_thr_t ct
[SPLAT_CONDVAR_TEST_COUNT
];
175 cv
.cv_magic
= SPLAT_CONDVAR_TEST_MAGIC
;
177 mutex_init(&cv
.cv_mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
178 cv_init(&cv
.cv_condvar
, NULL
, CV_DEFAULT
, NULL
);
180 /* Create some threads, the exact number isn't important just as
181 * long as we know how many we managed to create and should expect. */
182 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
184 ct
[i
].ct_name
= SPLAT_CONDVAR_TEST2_NAME
;
186 ct
[i
].ct_thread
= spl_kthread_create(splat_condvar_test12_thread
,
187 &ct
[i
], "%s/%d", SPLAT_CONDVAR_TEST_NAME
, i
);
189 if (!IS_ERR(ct
[i
].ct_thread
)) {
190 wake_up_process(ct
[i
].ct_thread
);
195 /* Wait until all threads are waiting on the condition variable */
196 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) != count
)
199 /* Wake all threads waiting on the condition variable */
200 cv_broadcast(&cv
.cv_condvar
);
202 /* Wait until all threads have exited */
203 while ((atomic_read(&cv
.cv_condvar
.cv_waiters
) > 0) || mutex_owner(&cv
.cv_mtx
))
206 splat_vprint(file
, SPLAT_CONDVAR_TEST2_NAME
, "Correctly woke all "
207 "%d sleeping threads at once\n", count
);
209 /* Wake everything for the failure case */
210 cv_destroy(&cv
.cv_condvar
);
212 /* wait for threads to exit */
213 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
214 if (!IS_ERR(ct
[i
].ct_thread
))
215 kthread_stop(ct
[i
].ct_thread
);
217 mutex_destroy(&cv
.cv_mtx
);
223 splat_condvar_test34_thread(void *arg
)
225 condvar_thr_t
*ct
= (condvar_thr_t
*)arg
;
226 condvar_priv_t
*cv
= ct
->ct_cvp
;
229 ASSERT(cv
->cv_magic
== SPLAT_CONDVAR_TEST_MAGIC
);
231 mutex_enter(&cv
->cv_mtx
);
232 splat_vprint(cv
->cv_file
, ct
->ct_name
,
233 "%s thread sleeping with %d waiters\n",
234 ct
->ct_thread
->comm
, atomic_read(&cv
->cv_condvar
.cv_waiters
));
236 /* Sleep no longer than 3 seconds, for this test we should
237 * actually never sleep that long without being woken up. */
238 rc
= cv_timedwait(&cv
->cv_condvar
, &cv
->cv_mtx
, lbolt
+ HZ
* 3);
240 ct
->ct_rc
= -ETIMEDOUT
;
241 splat_vprint(cv
->cv_file
, ct
->ct_name
, "%s thread timed out, "
242 "should have been woken\n", ct
->ct_thread
->comm
);
244 splat_vprint(cv
->cv_file
, ct
->ct_name
,
245 "%s thread woken %d waiters remain\n",
247 atomic_read(&cv
->cv_condvar
.cv_waiters
));
250 mutex_exit(&cv
->cv_mtx
);
252 /* wait for main thread reap us */
253 while (!kthread_should_stop())
259 splat_condvar_test3(struct file
*file
, void *arg
)
261 int i
, count
= 0, rc
= 0;
262 condvar_thr_t ct
[SPLAT_CONDVAR_TEST_COUNT
];
265 cv
.cv_magic
= SPLAT_CONDVAR_TEST_MAGIC
;
267 mutex_init(&cv
.cv_mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
268 cv_init(&cv
.cv_condvar
, NULL
, CV_DEFAULT
, NULL
);
270 /* Create some threads, the exact number isn't important just as
271 * long as we know how many we managed to create and should expect. */
272 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
274 ct
[i
].ct_name
= SPLAT_CONDVAR_TEST3_NAME
;
276 ct
[i
].ct_thread
= spl_kthread_create(splat_condvar_test34_thread
,
277 &ct
[i
], "%s/%d", SPLAT_CONDVAR_TEST_NAME
, i
);
279 if (!IS_ERR(ct
[i
].ct_thread
)) {
280 wake_up_process(ct
[i
].ct_thread
);
285 /* Wait until all threads are waiting on the condition variable */
286 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) != count
)
289 /* Wake a single thread at a time, wait until it exits */
290 for (i
= 1; i
<= count
; i
++) {
291 cv_signal(&cv
.cv_condvar
);
293 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) > (count
- i
))
296 /* Correct behavior 1 thread woken */
297 if (atomic_read(&cv
.cv_condvar
.cv_waiters
) == (count
- i
))
300 splat_vprint(file
, SPLAT_CONDVAR_TEST3_NAME
, "Attempted to "
301 "wake %d thread but work %d threads woke\n",
302 1, count
- atomic_read(&cv
.cv_condvar
.cv_waiters
));
307 /* Validate no waiting thread timed out early */
308 for (i
= 0; i
< count
; i
++)
313 splat_vprint(file
, SPLAT_CONDVAR_TEST3_NAME
, "Correctly woke "
314 "%d sleeping threads %d at a time\n", count
, 1);
316 /* Wait until that last nutex is dropped */
317 while (mutex_owner(&cv
.cv_mtx
))
320 /* Wake everything for the failure case */
321 cv_broadcast(&cv
.cv_condvar
);
322 cv_destroy(&cv
.cv_condvar
);
324 /* wait for threads to exit */
325 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
326 if (!IS_ERR(ct
[i
].ct_thread
))
327 kthread_stop(ct
[i
].ct_thread
);
329 mutex_destroy(&cv
.cv_mtx
);
335 splat_condvar_test4(struct file
*file
, void *arg
)
337 int i
, count
= 0, rc
= 0;
338 condvar_thr_t ct
[SPLAT_CONDVAR_TEST_COUNT
];
341 cv
.cv_magic
= SPLAT_CONDVAR_TEST_MAGIC
;
343 mutex_init(&cv
.cv_mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
344 cv_init(&cv
.cv_condvar
, NULL
, CV_DEFAULT
, NULL
);
346 /* Create some threads, the exact number isn't important just as
347 * long as we know how many we managed to create and should expect. */
348 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
350 ct
[i
].ct_name
= SPLAT_CONDVAR_TEST3_NAME
;
352 ct
[i
].ct_thread
= spl_kthread_create(splat_condvar_test34_thread
,
353 &ct
[i
], "%s/%d", SPLAT_CONDVAR_TEST_NAME
, i
);
355 if (!IS_ERR(ct
[i
].ct_thread
)) {
356 wake_up_process(ct
[i
].ct_thread
);
361 /* Wait until all threads are waiting on the condition variable */
362 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) != count
)
365 /* Wake a single thread at a time, wait until it exits */
366 for (i
= 1; i
<= count
; i
++) {
367 cv_signal(&cv
.cv_condvar
);
369 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) > (count
- i
))
372 /* Correct behavior 1 thread woken */
373 if (atomic_read(&cv
.cv_condvar
.cv_waiters
) == (count
- i
))
376 splat_vprint(file
, SPLAT_CONDVAR_TEST3_NAME
, "Attempted to "
377 "wake %d thread but work %d threads woke\n",
378 1, count
- atomic_read(&cv
.cv_condvar
.cv_waiters
));
383 /* Validate no waiting thread timed out early */
384 for (i
= 0; i
< count
; i
++)
389 splat_vprint(file
, SPLAT_CONDVAR_TEST3_NAME
, "Correctly woke "
390 "%d sleeping threads %d at a time\n", count
, 1);
392 /* Wait until that last nutex is dropped */
393 while (mutex_owner(&cv
.cv_mtx
))
396 /* Wake everything for the failure case */
397 cv_broadcast(&cv
.cv_condvar
);
398 cv_destroy(&cv
.cv_condvar
);
400 /* wait for threads to exit */
401 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
402 if (!IS_ERR(ct
[i
].ct_thread
))
403 kthread_stop(ct
[i
].ct_thread
);
405 mutex_destroy(&cv
.cv_mtx
);
411 splat_condvar_test5(struct file
*file
, void *arg
)
415 clock_t time_left
, time_before
, time_after
, time_delta
;
416 uint64_t whole_delta
;
417 uint32_t remain_delta
;
420 mutex_init(&mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
421 cv_init(&condvar
, NULL
, CV_DEFAULT
, NULL
);
423 splat_vprint(file
, SPLAT_CONDVAR_TEST5_NAME
, "Thread going to sleep for "
424 "%d second and expecting to be woken by timeout\n", 1);
426 /* Allow a 1 second timeout, plenty long to validate correctness. */
429 time_left
= cv_timedwait(&condvar
, &mtx
, lbolt
+ HZ
);
432 time_delta
= time_after
- time_before
; /* XXX - Handle jiffie wrap */
433 whole_delta
= time_delta
;
434 remain_delta
= do_div(whole_delta
, HZ
);
436 if (time_left
== -1) {
437 if (time_delta
>= HZ
) {
438 splat_vprint(file
, SPLAT_CONDVAR_TEST5_NAME
,
439 "Thread correctly timed out and was asleep "
440 "for %d.%d seconds (%d second min)\n",
441 (int)whole_delta
, (int)remain_delta
, 1);
443 splat_vprint(file
, SPLAT_CONDVAR_TEST5_NAME
,
444 "Thread correctly timed out but was only "
445 "asleep for %d.%d seconds (%d second "
446 "min)\n", (int)whole_delta
,
447 (int)remain_delta
, 1);
451 splat_vprint(file
, SPLAT_CONDVAR_TEST5_NAME
,
452 "Thread exited after only %d.%d seconds, it "
453 "did not hit the %d second timeout\n",
454 (int)whole_delta
, (int)remain_delta
, 1);
458 cv_destroy(&condvar
);
465 splat_condvar_init(void)
467 splat_subsystem_t
*sub
;
469 sub
= kmalloc(sizeof(*sub
), GFP_KERNEL
);
473 memset(sub
, 0, sizeof(*sub
));
474 strncpy(sub
->desc
.name
, SPLAT_CONDVAR_NAME
, SPLAT_NAME_SIZE
);
475 strncpy(sub
->desc
.desc
, SPLAT_CONDVAR_DESC
, SPLAT_DESC_SIZE
);
476 INIT_LIST_HEAD(&sub
->subsystem_list
);
477 INIT_LIST_HEAD(&sub
->test_list
);
478 spin_lock_init(&sub
->test_lock
);
479 sub
->desc
.id
= SPLAT_SUBSYSTEM_CONDVAR
;
481 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST1_NAME
, SPLAT_CONDVAR_TEST1_DESC
,
482 SPLAT_CONDVAR_TEST1_ID
, splat_condvar_test1
);
483 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST2_NAME
, SPLAT_CONDVAR_TEST2_DESC
,
484 SPLAT_CONDVAR_TEST2_ID
, splat_condvar_test2
);
485 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST3_NAME
, SPLAT_CONDVAR_TEST3_DESC
,
486 SPLAT_CONDVAR_TEST3_ID
, splat_condvar_test3
);
487 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST4_NAME
, SPLAT_CONDVAR_TEST4_DESC
,
488 SPLAT_CONDVAR_TEST4_ID
, splat_condvar_test4
);
489 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST5_NAME
, SPLAT_CONDVAR_TEST5_DESC
,
490 SPLAT_CONDVAR_TEST5_ID
, splat_condvar_test5
);
496 splat_condvar_fini(splat_subsystem_t
*sub
)
499 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST5_ID
);
500 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST4_ID
);
501 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST3_ID
);
502 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST2_ID
);
503 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST1_ID
);
509 splat_condvar_id(void) {
510 return SPLAT_SUBSYSTEM_CONDVAR
;