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
);
95 splat_condvar_test1(struct file
*file
, void *arg
)
97 int i
, count
= 0, rc
= 0;
98 condvar_thr_t ct
[SPLAT_CONDVAR_TEST_COUNT
];
101 cv
.cv_magic
= SPLAT_CONDVAR_TEST_MAGIC
;
103 mutex_init(&cv
.cv_mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
104 cv_init(&cv
.cv_condvar
, NULL
, CV_DEFAULT
, NULL
);
106 /* Create some threads, the exact number isn't important just as
107 * long as we know how many we managed to create and should expect. */
108 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
110 ct
[i
].ct_name
= SPLAT_CONDVAR_TEST1_NAME
;
112 ct
[i
].ct_thread
= spl_kthread_create(splat_condvar_test12_thread
,
113 &ct
[i
], "%s/%d", SPLAT_CONDVAR_TEST_NAME
, i
);
115 if (!IS_ERR(ct
[i
].ct_thread
)) {
116 wake_up_process(ct
[i
].ct_thread
);
121 /* Wait until all threads are waiting on the condition variable */
122 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) != count
)
125 /* Wake a single thread at a time, wait until it exits */
126 for (i
= 1; i
<= count
; i
++) {
127 cv_signal(&cv
.cv_condvar
);
129 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) > (count
- i
))
132 /* Correct behavior 1 thread woken */
133 if (atomic_read(&cv
.cv_condvar
.cv_waiters
) == (count
- i
))
136 splat_vprint(file
, SPLAT_CONDVAR_TEST1_NAME
, "Attempted to "
137 "wake %d thread but work %d threads woke\n",
138 1, count
- atomic_read(&cv
.cv_condvar
.cv_waiters
));
144 splat_vprint(file
, SPLAT_CONDVAR_TEST1_NAME
, "Correctly woke "
145 "%d sleeping threads %d at a time\n", count
, 1);
147 /* Wait until that last nutex is dropped */
148 while (mutex_owner(&cv
.cv_mtx
))
151 /* Wake everything for the failure case */
152 cv_broadcast(&cv
.cv_condvar
);
153 cv_destroy(&cv
.cv_condvar
);
154 mutex_destroy(&cv
.cv_mtx
);
160 splat_condvar_test2(struct file
*file
, void *arg
)
162 int i
, count
= 0, rc
= 0;
163 condvar_thr_t ct
[SPLAT_CONDVAR_TEST_COUNT
];
166 cv
.cv_magic
= SPLAT_CONDVAR_TEST_MAGIC
;
168 mutex_init(&cv
.cv_mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
169 cv_init(&cv
.cv_condvar
, NULL
, CV_DEFAULT
, NULL
);
171 /* Create some threads, the exact number isn't important just as
172 * long as we know how many we managed to create and should expect. */
173 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
175 ct
[i
].ct_name
= SPLAT_CONDVAR_TEST2_NAME
;
177 ct
[i
].ct_thread
= spl_kthread_create(splat_condvar_test12_thread
,
178 &ct
[i
], "%s/%d", SPLAT_CONDVAR_TEST_NAME
, i
);
180 if (!IS_ERR(ct
[i
].ct_thread
)) {
181 wake_up_process(ct
[i
].ct_thread
);
186 /* Wait until all threads are waiting on the condition variable */
187 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) != count
)
190 /* Wake all threads waiting on the condition variable */
191 cv_broadcast(&cv
.cv_condvar
);
193 /* Wait until all threads have exited */
194 while ((atomic_read(&cv
.cv_condvar
.cv_waiters
) > 0) || mutex_owner(&cv
.cv_mtx
))
197 splat_vprint(file
, SPLAT_CONDVAR_TEST2_NAME
, "Correctly woke all "
198 "%d sleeping threads at once\n", count
);
200 /* Wake everything for the failure case */
201 cv_destroy(&cv
.cv_condvar
);
202 mutex_destroy(&cv
.cv_mtx
);
208 splat_condvar_test34_thread(void *arg
)
210 condvar_thr_t
*ct
= (condvar_thr_t
*)arg
;
211 condvar_priv_t
*cv
= ct
->ct_cvp
;
214 ASSERT(cv
->cv_magic
== SPLAT_CONDVAR_TEST_MAGIC
);
216 mutex_enter(&cv
->cv_mtx
);
217 splat_vprint(cv
->cv_file
, ct
->ct_name
,
218 "%s thread sleeping with %d waiters\n",
219 ct
->ct_thread
->comm
, atomic_read(&cv
->cv_condvar
.cv_waiters
));
221 /* Sleep no longer than 3 seconds, for this test we should
222 * actually never sleep that long without being woken up. */
223 rc
= cv_timedwait(&cv
->cv_condvar
, &cv
->cv_mtx
, lbolt
+ HZ
* 3);
225 ct
->ct_rc
= -ETIMEDOUT
;
226 splat_vprint(cv
->cv_file
, ct
->ct_name
, "%s thread timed out, "
227 "should have been woken\n", ct
->ct_thread
->comm
);
229 splat_vprint(cv
->cv_file
, ct
->ct_name
,
230 "%s thread woken %d waiters remain\n",
232 atomic_read(&cv
->cv_condvar
.cv_waiters
));
235 mutex_exit(&cv
->cv_mtx
);
241 splat_condvar_test3(struct file
*file
, void *arg
)
243 int i
, count
= 0, rc
= 0;
244 condvar_thr_t ct
[SPLAT_CONDVAR_TEST_COUNT
];
247 cv
.cv_magic
= SPLAT_CONDVAR_TEST_MAGIC
;
249 mutex_init(&cv
.cv_mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
250 cv_init(&cv
.cv_condvar
, NULL
, CV_DEFAULT
, NULL
);
252 /* Create some threads, the exact number isn't important just as
253 * long as we know how many we managed to create and should expect. */
254 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
256 ct
[i
].ct_name
= SPLAT_CONDVAR_TEST3_NAME
;
258 ct
[i
].ct_thread
= spl_kthread_create(splat_condvar_test34_thread
,
259 &ct
[i
], "%s/%d", SPLAT_CONDVAR_TEST_NAME
, i
);
261 if (!IS_ERR(ct
[i
].ct_thread
)) {
262 wake_up_process(ct
[i
].ct_thread
);
267 /* Wait until all threads are waiting on the condition variable */
268 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) != count
)
271 /* Wake a single thread at a time, wait until it exits */
272 for (i
= 1; i
<= count
; i
++) {
273 cv_signal(&cv
.cv_condvar
);
275 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) > (count
- i
))
278 /* Correct behavior 1 thread woken */
279 if (atomic_read(&cv
.cv_condvar
.cv_waiters
) == (count
- i
))
282 splat_vprint(file
, SPLAT_CONDVAR_TEST3_NAME
, "Attempted to "
283 "wake %d thread but work %d threads woke\n",
284 1, count
- atomic_read(&cv
.cv_condvar
.cv_waiters
));
289 /* Validate no waiting thread timed out early */
290 for (i
= 0; i
< count
; i
++)
295 splat_vprint(file
, SPLAT_CONDVAR_TEST3_NAME
, "Correctly woke "
296 "%d sleeping threads %d at a time\n", count
, 1);
298 /* Wait until that last nutex is dropped */
299 while (mutex_owner(&cv
.cv_mtx
))
302 /* Wake everything for the failure case */
303 cv_broadcast(&cv
.cv_condvar
);
304 cv_destroy(&cv
.cv_condvar
);
305 mutex_destroy(&cv
.cv_mtx
);
311 splat_condvar_test4(struct file
*file
, void *arg
)
313 int i
, count
= 0, rc
= 0;
314 condvar_thr_t ct
[SPLAT_CONDVAR_TEST_COUNT
];
317 cv
.cv_magic
= SPLAT_CONDVAR_TEST_MAGIC
;
319 mutex_init(&cv
.cv_mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
320 cv_init(&cv
.cv_condvar
, NULL
, CV_DEFAULT
, NULL
);
322 /* Create some threads, the exact number isn't important just as
323 * long as we know how many we managed to create and should expect. */
324 for (i
= 0; i
< SPLAT_CONDVAR_TEST_COUNT
; i
++) {
326 ct
[i
].ct_name
= SPLAT_CONDVAR_TEST3_NAME
;
328 ct
[i
].ct_thread
= spl_kthread_create(splat_condvar_test34_thread
,
329 &ct
[i
], "%s/%d", SPLAT_CONDVAR_TEST_NAME
, i
);
331 if (!IS_ERR(ct
[i
].ct_thread
)) {
332 wake_up_process(ct
[i
].ct_thread
);
337 /* Wait until all threads are waiting on the condition variable */
338 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) != count
)
341 /* Wake a single thread at a time, wait until it exits */
342 for (i
= 1; i
<= count
; i
++) {
343 cv_signal(&cv
.cv_condvar
);
345 while (atomic_read(&cv
.cv_condvar
.cv_waiters
) > (count
- i
))
348 /* Correct behavior 1 thread woken */
349 if (atomic_read(&cv
.cv_condvar
.cv_waiters
) == (count
- i
))
352 splat_vprint(file
, SPLAT_CONDVAR_TEST3_NAME
, "Attempted to "
353 "wake %d thread but work %d threads woke\n",
354 1, count
- atomic_read(&cv
.cv_condvar
.cv_waiters
));
359 /* Validate no waiting thread timed out early */
360 for (i
= 0; i
< count
; i
++)
365 splat_vprint(file
, SPLAT_CONDVAR_TEST3_NAME
, "Correctly woke "
366 "%d sleeping threads %d at a time\n", count
, 1);
368 /* Wait until that last nutex is dropped */
369 while (mutex_owner(&cv
.cv_mtx
))
372 /* Wake everything for the failure case */
373 cv_broadcast(&cv
.cv_condvar
);
374 cv_destroy(&cv
.cv_condvar
);
375 mutex_destroy(&cv
.cv_mtx
);
381 splat_condvar_test5(struct file
*file
, void *arg
)
385 clock_t time_left
, time_before
, time_after
, time_delta
;
387 int32_t remain_delta
;
390 mutex_init(&mtx
, SPLAT_CONDVAR_TEST_NAME
, MUTEX_DEFAULT
, NULL
);
391 cv_init(&condvar
, NULL
, CV_DEFAULT
, NULL
);
393 splat_vprint(file
, SPLAT_CONDVAR_TEST5_NAME
, "Thread going to sleep for "
394 "%d second and expecting to be woken by timeout\n", 1);
396 /* Allow a 1 second timeout, plenty long to validate correctness. */
399 time_left
= cv_timedwait(&condvar
, &mtx
, lbolt
+ HZ
);
402 time_delta
= time_after
- time_before
; /* XXX - Handle jiffie wrap */
403 whole_delta
= time_delta
;
404 remain_delta
= do_div(whole_delta
, HZ
);
406 if (time_left
== -1) {
407 if (time_delta
>= HZ
) {
408 splat_vprint(file
, SPLAT_CONDVAR_TEST5_NAME
,
409 "Thread correctly timed out and was asleep "
410 "for %d.%d seconds (%d second min)\n",
411 (int)whole_delta
, remain_delta
, 1);
413 splat_vprint(file
, SPLAT_CONDVAR_TEST5_NAME
,
414 "Thread correctly timed out but was only "
415 "asleep for %d.%d seconds (%d second "
416 "min)\n", (int)whole_delta
, remain_delta
, 1);
420 splat_vprint(file
, SPLAT_CONDVAR_TEST5_NAME
,
421 "Thread exited after only %d.%d seconds, it "
422 "did not hit the %d second timeout\n",
423 (int)whole_delta
, remain_delta
, 1);
427 cv_destroy(&condvar
);
434 splat_condvar_init(void)
436 splat_subsystem_t
*sub
;
438 sub
= kmalloc(sizeof(*sub
), GFP_KERNEL
);
442 memset(sub
, 0, sizeof(*sub
));
443 strncpy(sub
->desc
.name
, SPLAT_CONDVAR_NAME
, SPLAT_NAME_SIZE
);
444 strncpy(sub
->desc
.desc
, SPLAT_CONDVAR_DESC
, SPLAT_DESC_SIZE
);
445 INIT_LIST_HEAD(&sub
->subsystem_list
);
446 INIT_LIST_HEAD(&sub
->test_list
);
447 spin_lock_init(&sub
->test_lock
);
448 sub
->desc
.id
= SPLAT_SUBSYSTEM_CONDVAR
;
450 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST1_NAME
, SPLAT_CONDVAR_TEST1_DESC
,
451 SPLAT_CONDVAR_TEST1_ID
, splat_condvar_test1
);
452 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST2_NAME
, SPLAT_CONDVAR_TEST2_DESC
,
453 SPLAT_CONDVAR_TEST2_ID
, splat_condvar_test2
);
454 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST3_NAME
, SPLAT_CONDVAR_TEST3_DESC
,
455 SPLAT_CONDVAR_TEST3_ID
, splat_condvar_test3
);
456 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST4_NAME
, SPLAT_CONDVAR_TEST4_DESC
,
457 SPLAT_CONDVAR_TEST4_ID
, splat_condvar_test4
);
458 SPLAT_TEST_INIT(sub
, SPLAT_CONDVAR_TEST5_NAME
, SPLAT_CONDVAR_TEST5_DESC
,
459 SPLAT_CONDVAR_TEST5_ID
, splat_condvar_test5
);
465 splat_condvar_fini(splat_subsystem_t
*sub
)
468 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST5_ID
);
469 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST4_ID
);
470 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST3_ID
);
471 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST2_ID
);
472 SPLAT_TEST_FINI(sub
, SPLAT_CONDVAR_TEST1_ID
);
478 splat_condvar_id(void) {
479 return SPLAT_SUBSYSTEM_CONDVAR
;