]> git.proxmox.com Git - mirror_spl.git/blame - module/splat/splat-mutex.c
Disable automatic log dumping
[mirror_spl.git] / module / splat / splat-mutex.c
CommitLineData
716154c5
BB
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>.
715f6251 6 * UCRL-CODE-235197
7 *
716154c5
BB
8 * This file is part of the SPL, Solaris Porting Layer.
9 * For details, see <http://github.com/behlendorf/spl/>.
10 *
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.
715f6251 15 *
716154c5 16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
715f6251 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
716154c5
BB
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting LAyer Tests (SPLAT) Mutex Tests.
25\*****************************************************************************/
715f6251 26
df870a69
BB
27#include <sys/mutex.h>
28#include <sys/taskq.h>
7c50328b 29#include "splat-internal.h"
f1ca4da6 30
4d54fdee
BB
31#define SPLAT_MUTEX_NAME "mutex"
32#define SPLAT_MUTEX_DESC "Kernel Mutex Tests"
f1ca4da6 33
4d54fdee
BB
34#define SPLAT_MUTEX_TEST1_ID 0x0401
35#define SPLAT_MUTEX_TEST1_NAME "tryenter"
36#define SPLAT_MUTEX_TEST1_DESC "Validate mutex_tryenter() correctness"
f1ca4da6 37
4d54fdee
BB
38#define SPLAT_MUTEX_TEST2_ID 0x0402
39#define SPLAT_MUTEX_TEST2_NAME "race"
40#define SPLAT_MUTEX_TEST2_DESC "Many threads entering/exiting the mutex"
f1ca4da6 41
4d54fdee
BB
42#define SPLAT_MUTEX_TEST3_ID 0x0403
43#define SPLAT_MUTEX_TEST3_NAME "owned"
44#define SPLAT_MUTEX_TEST3_DESC "Validate mutex_owned() correctness"
f1ca4da6 45
4d54fdee
BB
46#define SPLAT_MUTEX_TEST4_ID 0x0404
47#define SPLAT_MUTEX_TEST4_NAME "owner"
48#define SPLAT_MUTEX_TEST4_DESC "Validate mutex_owner() correctness"
f1ca4da6 49
4d54fdee
BB
50#define SPLAT_MUTEX_TEST_MAGIC 0x115599DDUL
51#define SPLAT_MUTEX_TEST_NAME "mutex_test"
52#define SPLAT_MUTEX_TEST_TASKQ "mutex_taskq"
53#define SPLAT_MUTEX_TEST_COUNT 128
f1ca4da6 54
55typedef struct mutex_priv {
56 unsigned long mp_magic;
57 struct file *mp_file;
4d54fdee
BB
58 kmutex_t mp_mtx;
59 int mp_rc;
ede0bdff 60 int mp_rc2;
f1ca4da6 61} mutex_priv_t;
62
f1ca4da6 63static void
5b5f5685 64splat_mutex_test1_func(void *arg)
f1ca4da6 65{
4d54fdee
BB
66 mutex_priv_t *mp = (mutex_priv_t *)arg;
67 ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
68
69 if (mutex_tryenter(&mp->mp_mtx)) {
70 mp->mp_rc = 0;
71 mutex_exit(&mp->mp_mtx);
72 } else {
73 mp->mp_rc = -EBUSY;
74 }
f1ca4da6 75}
76
77static int
7c50328b 78splat_mutex_test1(struct file *file, void *arg)
f1ca4da6 79{
4d54fdee
BB
80 mutex_priv_t *mp;
81 taskq_t *tq;
82 int id, rc = 0;
83
84 mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
85 if (mp == NULL)
86 return -ENOMEM;
87
88 tq = taskq_create(SPLAT_MUTEX_TEST_TASKQ, 1, maxclsyspri,
89 50, INT_MAX, TASKQ_PREPOPULATE);
90 if (tq == NULL) {
91 rc = -ENOMEM;
92 goto out2;
93 }
94
95 mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
96 mp->mp_file = file;
97 mutex_init(&mp->mp_mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
98 mutex_enter(&mp->mp_mtx);
99
100 /*
101 * Schedule a task function which will try and acquire the mutex via
102 * mutex_tryenter() while it's held. This should fail and the task
103 * function will indicate this status in the passed private data.
104 */
105 mp->mp_rc = -EINVAL;
106 id = taskq_dispatch(tq, splat_mutex_test1_func, mp, TQ_SLEEP);
107 if (id == 0) {
108 mutex_exit(&mp->mp_mtx);
109 splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
110 "taskq_dispatch() failed\n");
111 rc = -EINVAL;
112 goto out;
113 }
114
115 taskq_wait_id(tq, id);
116 mutex_exit(&mp->mp_mtx);
117
118 /* Task function successfully acquired mutex, very bad! */
119 if (mp->mp_rc != -EBUSY) {
120 splat_vprint(file, SPLAT_MUTEX_TEST1_NAME,
121 "mutex_trylock() incorrectly succeeded when "
122 "the mutex was held, %d/%d\n", id, mp->mp_rc);
123 rc = -EINVAL;
124 goto out;
125 } else {
126 splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
127 "mutex_trylock() correctly failed when "
128 "the mutex was held\n");
129 }
130
131 /*
132 * Schedule a task function which will try and acquire the mutex via
133 * mutex_tryenter() while it is not held. This should succeed and
134 * can be verified by checking the private data.
135 */
136 mp->mp_rc = -EINVAL;
137 id = taskq_dispatch(tq, splat_mutex_test1_func, mp, TQ_SLEEP);
138 if (id == 0) {
139 splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
140 "taskq_dispatch() failed\n");
141 rc = -EINVAL;
142 goto out;
143 }
144
145 taskq_wait_id(tq, id);
146
147 /* Task function failed to acquire mutex, very bad! */
148 if (mp->mp_rc != 0) {
149 splat_vprint(file, SPLAT_MUTEX_TEST1_NAME,
150 "mutex_trylock() incorrectly failed when "
151 "the mutex was not held, %d/%d\n", id, mp->mp_rc);
152 rc = -EINVAL;
153 } else {
154 splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
155 "mutex_trylock() correctly succeeded "
156 "when the mutex was not held\n");
157 }
f1ca4da6 158out:
4d54fdee
BB
159 taskq_destroy(tq);
160 mutex_destroy(&(mp->mp_mtx));
f1ca4da6 161out2:
4d54fdee
BB
162 kfree(mp);
163 return rc;
f1ca4da6 164}
165
166static void
5b5f5685 167splat_mutex_test2_func(void *arg)
f1ca4da6 168{
4d54fdee
BB
169 mutex_priv_t *mp = (mutex_priv_t *)arg;
170 int rc;
171 ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
172
173 /* Read the value before sleeping and write it after we wake up to
174 * maximize the chance of a race if mutexs are not working properly */
175 mutex_enter(&mp->mp_mtx);
176 rc = mp->mp_rc;
177 set_current_state(TASK_INTERRUPTIBLE);
178 schedule_timeout(HZ / 100); /* 1/100 of a second */
179 VERIFY(mp->mp_rc == rc);
180 mp->mp_rc = rc + 1;
181 mutex_exit(&mp->mp_mtx);
f1ca4da6 182}
183
184static int
7c50328b 185splat_mutex_test2(struct file *file, void *arg)
f1ca4da6 186{
4d54fdee
BB
187 mutex_priv_t *mp;
188 taskq_t *tq;
189 int i, rc = 0;
190
191 mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
192 if (mp == NULL)
193 return -ENOMEM;
194
195 /* Create several threads allowing tasks to race with each other */
196 tq = taskq_create(SPLAT_MUTEX_TEST_TASKQ, num_online_cpus(),
197 maxclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE);
198 if (tq == NULL) {
199 rc = -ENOMEM;
200 goto out;
201 }
202
203 mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
204 mp->mp_file = file;
205 mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
206 mp->mp_rc = 0;
207
208 /*
209 * Schedule N work items to the work queue each of which enters the
210 * mutex, sleeps briefly, then exits the mutex. On a multiprocessor
211 * box these work items will be handled by all available CPUs. The
212 * task function checks to ensure the tracked shared variable is
213 * always only incremented by one. Additionally, the mutex itself
214 * is instrumented such that if any two processors are in the
215 * critical region at the same time the system will panic. If the
216 * mutex is implemented right this will never happy, that's a pass.
217 */
218 for (i = 0; i < SPLAT_MUTEX_TEST_COUNT; i++) {
219 if (!taskq_dispatch(tq, splat_mutex_test2_func, mp, TQ_SLEEP)) {
220 splat_vprint(file, SPLAT_MUTEX_TEST2_NAME,
221 "Failed to queue task %d\n", i);
222 rc = -EINVAL;
223 }
224 }
225
226 taskq_wait(tq);
227
228 if (mp->mp_rc == SPLAT_MUTEX_TEST_COUNT) {
229 splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads "
230 "correctly entered/exited the mutex %d times\n",
231 num_online_cpus(), mp->mp_rc);
232 } else {
233 splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads "
234 "only processed %d/%d mutex work items\n",
235 num_online_cpus(),mp->mp_rc,SPLAT_MUTEX_TEST_COUNT);
236 rc = -EINVAL;
237 }
238
239 taskq_destroy(tq);
240 mutex_destroy(&(mp->mp_mtx));
f1ca4da6 241out:
4d54fdee
BB
242 kfree(mp);
243 return rc;
f1ca4da6 244}
245
ede0bdff
BB
246static void
247splat_mutex_owned(void *priv)
248{
249 mutex_priv_t *mp = (mutex_priv_t *)priv;
250
251 ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
252 mp->mp_rc = mutex_owned(&mp->mp_mtx);
253 mp->mp_rc2 = MUTEX_HELD(&mp->mp_mtx);
254}
255
f1ca4da6 256static int
7c50328b 257splat_mutex_test3(struct file *file, void *arg)
f1ca4da6 258{
ede0bdff
BB
259 mutex_priv_t mp;
260 taskq_t *tq;
4d54fdee 261 int rc = 0;
f1ca4da6 262
ede0bdff
BB
263 mp.mp_magic = SPLAT_MUTEX_TEST_MAGIC;
264 mp.mp_file = file;
265 mutex_init(&mp.mp_mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
266
267 if ((tq = taskq_create(SPLAT_MUTEX_TEST_NAME, 1, maxclsyspri,
268 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
269 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Taskq '%s' "
270 "create failed\n", SPLAT_MUTEX_TEST3_NAME);
271 return -EINVAL;
272 }
273
274 mutex_enter(&mp.mp_mtx);
f1ca4da6 275
4d54fdee 276 /* Mutex should be owned by current */
ede0bdff 277 if (!mutex_owned(&mp.mp_mtx)) {
4d54fdee 278 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Unowned mutex "
ede0bdff
BB
279 "should be owned by pid %d\n", current->pid);
280 rc = -EINVAL;
281 goto out_exit;
282 }
283
284 if (taskq_dispatch(tq, splat_mutex_owned, &mp, TQ_SLEEP) == 0) {
285 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Failed to "
286 "dispatch function '%s' to taskq\n",
287 sym2str(splat_mutex_owned));
288 rc = -EINVAL;
289 goto out_exit;
290 }
291 taskq_wait(tq);
292
293 /* Mutex should not be owned which checked from a different thread */
294 if (mp.mp_rc || mp.mp_rc2) {
295 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
296 "pid %d not by taskq\n", current->pid);
297 rc = -EINVAL;
298 goto out_exit;
299 }
300
301 mutex_exit(&mp.mp_mtx);
302
303 /* Mutex should not be owned by current */
304 if (mutex_owned(&mp.mp_mtx)) {
305 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
306 "pid %d it should be unowned\b", current->pid);
4d54fdee
BB
307 rc = -EINVAL;
308 goto out;
309 }
f1ca4da6 310
ede0bdff
BB
311 if (taskq_dispatch(tq, splat_mutex_owned, &mp, TQ_SLEEP) == 0) {
312 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Failed to "
313 "dispatch function '%s' to taskq\n",
314 sym2str(splat_mutex_owned));
315 rc = -EINVAL;
316 goto out;
317 }
318 taskq_wait(tq);
f1ca4da6 319
ede0bdff
BB
320 /* Mutex should be owned by no one */
321 if (mp.mp_rc || mp.mp_rc2) {
4d54fdee 322 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
ede0bdff 323 "no one, %d/%d disagrees\n", mp.mp_rc, mp.mp_rc2);
4d54fdee
BB
324 rc = -EINVAL;
325 goto out;
326 }
f1ca4da6 327
7c50328b 328 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s",
4d54fdee 329 "Correct mutex_owned() behavior\n");
ede0bdff
BB
330 goto out;
331out_exit:
332 mutex_exit(&mp.mp_mtx);
f1ca4da6 333out:
ede0bdff
BB
334 mutex_destroy(&mp.mp_mtx);
335 taskq_destroy(tq);
f1ca4da6 336
4d54fdee 337 return rc;
f1ca4da6 338}
339
340static int
7c50328b 341splat_mutex_test4(struct file *file, void *arg)
f1ca4da6 342{
343 kmutex_t mtx;
4d54fdee
BB
344 kthread_t *owner;
345 int rc = 0;
346
347 mutex_init(&mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
ede0bdff
BB
348
349 /*
350 * Verify mutex owner is cleared after being dropped. Depending
351 * on how you build your kernel this behavior changes, ensure the
352 * SPL mutex implementation is properly detecting this.
353 */
354 mutex_enter(&mtx);
355 msleep(100);
356 mutex_exit(&mtx);
357 if (MUTEX_HELD(&mtx)) {
358 splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should "
359 "not be held, bit is by %p\n", mutex_owner(&mtx));
360 rc = -EINVAL;
361 goto out;
362 }
363
4d54fdee
BB
364 mutex_enter(&mtx);
365
366 /* Mutex should be owned by current */
367 owner = mutex_owner(&mtx);
368 if (current != owner) {
ede0bdff 369 splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should "
4d54fdee
BB
370 "be owned by pid %d but is owned by pid %d\n",
371 current->pid, owner ? owner->pid : -1);
372 rc = -EINVAL;
373 goto out;
374 }
375
376 mutex_exit(&mtx);
377
378 /* Mutex should not be owned by any task */
379 owner = mutex_owner(&mtx);
380 if (owner) {
ede0bdff 381 splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should not "
4d54fdee
BB
382 "be owned but is owned by pid %d\n", owner->pid);
383 rc = -EINVAL;
384 goto out;
385 }
f1ca4da6 386
7c50328b 387 splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s",
4d54fdee 388 "Correct mutex_owner() behavior\n");
f1ca4da6 389out:
4d54fdee 390 mutex_destroy(&mtx);
f1ca4da6 391
4d54fdee 392 return rc;
f1ca4da6 393}
394
7c50328b 395splat_subsystem_t *
396splat_mutex_init(void)
f1ca4da6 397{
7c50328b 398 splat_subsystem_t *sub;
f1ca4da6 399
400 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
401 if (sub == NULL)
402 return NULL;
403
404 memset(sub, 0, sizeof(*sub));
7c50328b 405 strncpy(sub->desc.name, SPLAT_MUTEX_NAME, SPLAT_NAME_SIZE);
406 strncpy(sub->desc.desc, SPLAT_MUTEX_DESC, SPLAT_DESC_SIZE);
f1ca4da6 407 INIT_LIST_HEAD(&sub->subsystem_list);
408 INIT_LIST_HEAD(&sub->test_list);
409 spin_lock_init(&sub->test_lock);
7c50328b 410 sub->desc.id = SPLAT_SUBSYSTEM_MUTEX;
f1ca4da6 411
7c50328b 412 SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST1_NAME, SPLAT_MUTEX_TEST1_DESC,
413 SPLAT_MUTEX_TEST1_ID, splat_mutex_test1);
414 SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST2_NAME, SPLAT_MUTEX_TEST2_DESC,
415 SPLAT_MUTEX_TEST2_ID, splat_mutex_test2);
416 SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST3_NAME, SPLAT_MUTEX_TEST3_DESC,
417 SPLAT_MUTEX_TEST3_ID, splat_mutex_test3);
418 SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST4_NAME, SPLAT_MUTEX_TEST4_DESC,
419 SPLAT_MUTEX_TEST4_ID, splat_mutex_test4);
f1ca4da6 420
421 return sub;
422}
423
424void
7c50328b 425splat_mutex_fini(splat_subsystem_t *sub)
f1ca4da6 426{
427 ASSERT(sub);
7c50328b 428 SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST4_ID);
429 SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST3_ID);
430 SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST2_ID);
431 SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST1_ID);
f1ca4da6 432
433 kfree(sub);
434}
435
436int
7c50328b 437splat_mutex_id(void) {
438 return SPLAT_SUBSYSTEM_MUTEX;
f1ca4da6 439}