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