]> git.proxmox.com Git - mirror_spl.git/blame - module/splat/splat-rwlock.c
Add defclsyspri macro
[mirror_spl.git] / module / splat / splat-rwlock.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) Read/Writer Lock Tests.
25\*****************************************************************************/
715f6251 26
52479ecf 27#include <sys/random.h>
df870a69
BB
28#include <sys/rwlock.h>
29#include <sys/taskq.h>
52479ecf
NB
30#include <linux/delay.h>
31#include <linux/mm_compat.h>
7c50328b 32#include "splat-internal.h"
f1ca4da6 33
7c50328b 34#define SPLAT_RWLOCK_NAME "rwlock"
35#define SPLAT_RWLOCK_DESC "Kernel RW Lock Tests"
f1ca4da6 36
7c50328b 37#define SPLAT_RWLOCK_TEST1_ID 0x0701
e811949a
BB
38#define SPLAT_RWLOCK_TEST1_NAME "N-rd/1-wr"
39#define SPLAT_RWLOCK_TEST1_DESC "Multiple readers one writer"
f1ca4da6 40
7c50328b 41#define SPLAT_RWLOCK_TEST2_ID 0x0702
e811949a
BB
42#define SPLAT_RWLOCK_TEST2_NAME "0-rd/N-wr"
43#define SPLAT_RWLOCK_TEST2_DESC "Multiple writers"
f1ca4da6 44
7c50328b 45#define SPLAT_RWLOCK_TEST3_ID 0x0703
e811949a
BB
46#define SPLAT_RWLOCK_TEST3_NAME "held"
47#define SPLAT_RWLOCK_TEST3_DESC "RW_{LOCK|READ|WRITE}_HELD"
f1ca4da6 48
7c50328b 49#define SPLAT_RWLOCK_TEST4_ID 0x0704
e811949a
BB
50#define SPLAT_RWLOCK_TEST4_NAME "tryenter"
51#define SPLAT_RWLOCK_TEST4_DESC "Tryenter"
f1ca4da6 52
7c50328b 53#define SPLAT_RWLOCK_TEST5_ID 0x0705
e811949a
BB
54#define SPLAT_RWLOCK_TEST5_NAME "rw_downgrade"
55#define SPLAT_RWLOCK_TEST5_DESC "Write downgrade"
f1ca4da6 56
7c50328b 57#define SPLAT_RWLOCK_TEST6_ID 0x0706
e811949a
BB
58#define SPLAT_RWLOCK_TEST6_NAME "rw_tryupgrade"
59#define SPLAT_RWLOCK_TEST6_DESC "Read upgrade"
f1ca4da6 60
7c50328b 61#define SPLAT_RWLOCK_TEST_MAGIC 0x115599DDUL
62#define SPLAT_RWLOCK_TEST_NAME "rwlock_test"
e811949a 63#define SPLAT_RWLOCK_TEST_TASKQ "rwlock_taskq"
7c50328b 64#define SPLAT_RWLOCK_TEST_COUNT 8
f1ca4da6 65
7c50328b 66#define SPLAT_RWLOCK_RELEASE_INIT 0
e811949a
BB
67#define SPLAT_RWLOCK_RELEASE_WR 1
68#define SPLAT_RWLOCK_RELEASE_RD 2
f1ca4da6 69
70typedef struct rw_priv {
e811949a
BB
71 unsigned long rw_magic;
72 struct file *rw_file;
73 krwlock_t rw_rwlock;
74 spinlock_t rw_lock;
f1ca4da6 75 wait_queue_head_t rw_waitq;
e811949a
BB
76 int rw_completed;
77 int rw_holders;
78 int rw_waiters;
79 int rw_release;
80 int rw_rc;
1e18307b 81 krw_t rw_type;
f1ca4da6 82} rw_priv_t;
83
84typedef struct rw_thr {
f1ca4da6 85 const char *rwt_name;
86 rw_priv_t *rwt_rwp;
b84412a6 87 struct task_struct *rwt_thread;
f1ca4da6 88} rw_thr_t;
89
e811949a 90void splat_init_rw_priv(rw_priv_t *rwp, struct file *file)
f1ca4da6 91{
e811949a
BB
92 rwp->rw_magic = SPLAT_RWLOCK_TEST_MAGIC;
93 rwp->rw_file = file;
94 rw_init(&rwp->rw_rwlock, SPLAT_RWLOCK_TEST_NAME, RW_DEFAULT, NULL);
95 spin_lock_init(&rwp->rw_lock);
96 init_waitqueue_head(&rwp->rw_waitq);
97 rwp->rw_completed = 0;
98 rwp->rw_holders = 0;
99 rwp->rw_waiters = 0;
100 rwp->rw_release = SPLAT_RWLOCK_RELEASE_INIT;
101 rwp->rw_rc = 0;
102 rwp->rw_type = 0;
f1ca4da6 103}
104
e811949a
BB
105static int
106splat_rwlock_wr_thr(void *arg)
f1ca4da6 107{
108 rw_thr_t *rwt = (rw_thr_t *)arg;
e811949a
BB
109 rw_priv_t *rwp = rwt->rwt_rwp;
110 uint8_t rnd;
f1ca4da6 111
e811949a 112 ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
b84412a6 113
f1ca4da6 114 get_random_bytes((void *)&rnd, 1);
e811949a
BB
115 msleep((unsigned int)rnd);
116
117 splat_vprint(rwp->rw_file, rwt->rwt_name,
b84412a6
BB
118 "%s trying to acquire rwlock (%d holding/%d waiting)\n",
119 rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
e811949a
BB
120 spin_lock(&rwp->rw_lock);
121 rwp->rw_waiters++;
122 spin_unlock(&rwp->rw_lock);
123 rw_enter(&rwp->rw_rwlock, RW_WRITER);
124
125 spin_lock(&rwp->rw_lock);
126 rwp->rw_waiters--;
127 rwp->rw_holders++;
128 spin_unlock(&rwp->rw_lock);
129 splat_vprint(rwp->rw_file, rwt->rwt_name,
b84412a6
BB
130 "%s acquired rwlock (%d holding/%d waiting)\n",
131 rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
e811949a
BB
132
133 /* Wait for control thread to signal we can release the write lock */
134 wait_event_interruptible(rwp->rw_waitq, splat_locked_test(&rwp->rw_lock,
b84412a6 135 rwp->rw_release == SPLAT_RWLOCK_RELEASE_WR));
e811949a
BB
136
137 spin_lock(&rwp->rw_lock);
138 rwp->rw_completed++;
139 rwp->rw_holders--;
140 spin_unlock(&rwp->rw_lock);
141 splat_vprint(rwp->rw_file, rwt->rwt_name,
b84412a6
BB
142 "%s dropped rwlock (%d holding/%d waiting)\n",
143 rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
e811949a
BB
144
145 rw_exit(&rwp->rw_rwlock);
146
f1ca4da6 147 return 0;
148}
149
e811949a
BB
150static int
151splat_rwlock_rd_thr(void *arg)
f1ca4da6 152{
153 rw_thr_t *rwt = (rw_thr_t *)arg;
e811949a
BB
154 rw_priv_t *rwp = rwt->rwt_rwp;
155 uint8_t rnd;
f1ca4da6 156
e811949a 157 ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
b84412a6 158
f1ca4da6 159 get_random_bytes((void *)&rnd, 1);
e811949a
BB
160 msleep((unsigned int)rnd);
161
162 /* Don't try and take the semaphore until after someone has it */
b84412a6
BB
163 wait_event_interruptible(rwp->rw_waitq,
164 splat_locked_test(&rwp->rw_lock, rwp->rw_holders > 0));
e811949a
BB
165
166 splat_vprint(rwp->rw_file, rwt->rwt_name,
b84412a6
BB
167 "%s trying to acquire rwlock (%d holding/%d waiting)\n",
168 rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
e811949a
BB
169 spin_lock(&rwp->rw_lock);
170 rwp->rw_waiters++;
171 spin_unlock(&rwp->rw_lock);
172 rw_enter(&rwp->rw_rwlock, RW_READER);
173
174 spin_lock(&rwp->rw_lock);
175 rwp->rw_waiters--;
176 rwp->rw_holders++;
177 spin_unlock(&rwp->rw_lock);
178 splat_vprint(rwp->rw_file, rwt->rwt_name,
b84412a6
BB
179 "%s acquired rwlock (%d holding/%d waiting)\n",
180 rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
e811949a
BB
181
182 /* Wait for control thread to signal we can release the read lock */
183 wait_event_interruptible(rwp->rw_waitq, splat_locked_test(&rwp->rw_lock,
b84412a6 184 rwp->rw_release == SPLAT_RWLOCK_RELEASE_RD));
e811949a
BB
185
186 spin_lock(&rwp->rw_lock);
187 rwp->rw_completed++;
188 rwp->rw_holders--;
189 spin_unlock(&rwp->rw_lock);
190 splat_vprint(rwp->rw_file, rwt->rwt_name,
b84412a6
BB
191 "%s dropped rwlock (%d holding/%d waiting)\n",
192 rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
e811949a
BB
193
194 rw_exit(&rwp->rw_rwlock);
195
f1ca4da6 196 return 0;
197}
198
199static int
7c50328b 200splat_rwlock_test1(struct file *file, void *arg)
f1ca4da6 201{
202 int i, count = 0, rc = 0;
7c50328b 203 rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT];
e811949a 204 rw_priv_t *rwp;
f1ca4da6 205
e811949a
BB
206 rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
207 if (rwp == NULL)
208 return -ENOMEM;
209
210 splat_init_rw_priv(rwp, file);
f1ca4da6 211
212 /* Create some threads, the exact number isn't important just as
213 * long as we know how many we managed to create and should expect. */
7c50328b 214 for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
e811949a 215 rwt[i].rwt_rwp = rwp;
7c50328b 216 rwt[i].rwt_name = SPLAT_RWLOCK_TEST1_NAME;
7c50328b 217
e811949a
BB
218 /* The first thread will be the writer */
219 if (i == 0)
17a527cb 220 rwt[i].rwt_thread = spl_kthread_create(splat_rwlock_wr_thr,
b84412a6 221 &rwt[i], "%s/%d", SPLAT_RWLOCK_TEST_NAME, i);
e811949a 222 else
17a527cb 223 rwt[i].rwt_thread = spl_kthread_create(splat_rwlock_rd_thr,
b84412a6 224 &rwt[i], "%s/%d", SPLAT_RWLOCK_TEST_NAME, i);
e811949a 225
b84412a6
BB
226 if (!IS_ERR(rwt[i].rwt_thread)) {
227 wake_up_process(rwt[i].rwt_thread);
f1ca4da6 228 count++;
b84412a6 229 }
f1ca4da6 230 }
231
e811949a
BB
232 /* Wait for the writer */
233 while (splat_locked_test(&rwp->rw_lock, rwp->rw_holders == 0)) {
234 wake_up_interruptible(&rwp->rw_waitq);
235 msleep(100);
f1ca4da6 236 }
f1ca4da6 237
e811949a
BB
238 /* Wait for 'count-1' readers */
239 while (splat_locked_test(&rwp->rw_lock, rwp->rw_waiters < count - 1)) {
240 wake_up_interruptible(&rwp->rw_waitq);
241 msleep(100);
242 }
f1ca4da6 243
e811949a
BB
244 /* Verify there is only one lock holder */
245 if (splat_locked_test(&rwp->rw_lock, rwp->rw_holders) != 1) {
246 splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only 1 holder "
247 "expected for rwlock (%d holding/%d waiting)\n",
248 rwp->rw_holders, rwp->rw_waiters);
249 rc = -EINVAL;
f1ca4da6 250 }
e811949a
BB
251
252 /* Verify 'count-1' readers */
253 if (splat_locked_test(&rwp->rw_lock, rwp->rw_waiters != count - 1)) {
254 splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only %d waiters "
255 "expected for rwlock (%d holding/%d waiting)\n",
256 count - 1, rwp->rw_holders, rwp->rw_waiters);
257 rc = -EINVAL;
f1ca4da6 258 }
f1ca4da6 259
e811949a
BB
260 /* Signal the writer to release, allows readers to acquire */
261 spin_lock(&rwp->rw_lock);
262 rwp->rw_release = SPLAT_RWLOCK_RELEASE_WR;
263 wake_up_interruptible(&rwp->rw_waitq);
264 spin_unlock(&rwp->rw_lock);
f1ca4da6 265
e811949a
BB
266 /* Wait for 'count-1' readers to hold the lock */
267 while (splat_locked_test(&rwp->rw_lock, rwp->rw_holders < count - 1)) {
268 wake_up_interruptible(&rwp->rw_waitq);
269 msleep(100);
f1ca4da6 270 }
271
e811949a
BB
272 /* Verify there are 'count-1' readers */
273 if (splat_locked_test(&rwp->rw_lock, rwp->rw_holders != count - 1)) {
274 splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only %d holders "
275 "expected for rwlock (%d holding/%d waiting)\n",
276 count - 1, rwp->rw_holders, rwp->rw_waiters);
277 rc = -EINVAL;
278 }
f1ca4da6 279
e811949a
BB
280 /* Release 'count-1' readers */
281 spin_lock(&rwp->rw_lock);
282 rwp->rw_release = SPLAT_RWLOCK_RELEASE_RD;
283 wake_up_interruptible(&rwp->rw_waitq);
284 spin_unlock(&rwp->rw_lock);
7c50328b 285
e811949a
BB
286 /* Wait for the test to complete */
287 while (splat_locked_test(&rwp->rw_lock,
288 rwp->rw_holders>0 || rwp->rw_waiters>0))
289 msleep(100);
f1ca4da6 290
e811949a
BB
291 rw_destroy(&(rwp->rw_rwlock));
292 kfree(rwp);
f1ca4da6 293
e811949a
BB
294 return rc;
295}
f1ca4da6 296
e811949a
BB
297static void
298splat_rwlock_test2_func(void *arg)
299{
300 rw_priv_t *rwp = (rw_priv_t *)arg;
301 int rc;
302 ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
303
304 /* Read the value before sleeping and write it after we wake up to
305 * maximize the chance of a race if rwlocks are not working properly */
306 rw_enter(&rwp->rw_rwlock, RW_WRITER);
307 rc = rwp->rw_rc;
308 set_current_state(TASK_INTERRUPTIBLE);
309 schedule_timeout(HZ / 100); /* 1/100 of a second */
310 VERIFY(rwp->rw_rc == rc);
311 rwp->rw_rc = rc + 1;
312 rw_exit(&rwp->rw_rwlock);
f1ca4da6 313}
314
315static int
7c50328b 316splat_rwlock_test2(struct file *file, void *arg)
f1ca4da6 317{
e811949a
BB
318 rw_priv_t *rwp;
319 taskq_t *tq;
320 int i, rc = 0, tq_count = 256;
f1ca4da6 321
e811949a
BB
322 rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
323 if (rwp == NULL)
324 return -ENOMEM;
f1ca4da6 325
e811949a 326 splat_init_rw_priv(rwp, file);
f1ca4da6 327
e811949a
BB
328 /* Create several threads allowing tasks to race with each other */
329 tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, num_online_cpus(),
62aa81a5 330 defclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE);
e811949a
BB
331 if (tq == NULL) {
332 rc = -ENOMEM;
333 goto out;
f1ca4da6 334 }
335
e811949a
BB
336 /*
337 * Schedule N work items to the work queue each of which enters the
338 * writer rwlock, sleeps briefly, then exits the writer rwlock. On a
339 * multiprocessor box these work items will be handled by all available
340 * CPUs. The task function checks to ensure the tracked shared variable
341 * is always only incremented by one. Additionally, the rwlock itself
342 * is instrumented such that if any two processors are in the
343 * critical region at the same time the system will panic. If the
344 * rwlock is implemented right this will never happy, that's a pass.
345 */
346 for (i = 0; i < tq_count; i++) {
347 if (!taskq_dispatch(tq,splat_rwlock_test2_func,rwp,TQ_SLEEP)) {
348 splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME,
349 "Failed to queue task %d\n", i);
350 rc = -EINVAL;
351 }
f1ca4da6 352 }
f1ca4da6 353
e811949a 354 taskq_wait(tq);
f1ca4da6 355
e811949a
BB
356 if (rwp->rw_rc == tq_count) {
357 splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads "
358 "correctly entered/exited the rwlock %d times\n",
359 num_online_cpus(), rwp->rw_rc);
360 } else {
361 splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads "
362 "only processed %d/%d w rwlock work items\n",
363 num_online_cpus(), rwp->rw_rc, tq_count);
364 rc = -EINVAL;
f1ca4da6 365 }
366
e811949a
BB
367 taskq_destroy(tq);
368 rw_destroy(&(rwp->rw_rwlock));
369out:
370 kfree(rwp);
f1ca4da6 371 return rc;
372}
373
e811949a
BB
374#define splat_rwlock_test3_helper(rwp,rex1,rex2,wex1,wex2,held_func,rc) \
375do { \
376 int result, _rc1_, _rc2_, _rc3_, _rc4_; \
377 \
378 rc = 0; \
379 rw_enter(&(rwp)->rw_rwlock, RW_READER); \
380 _rc1_ = ((result = held_func(&(rwp)->rw_rwlock)) != rex1); \
381 splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \
382 " returned %d (expected %d) when RW_READER\n", \
383 _rc1_ ? "Fail " : "", result, rex1); \
384 rw_exit(&(rwp)->rw_rwlock); \
385 _rc2_ = ((result = held_func(&(rwp)->rw_rwlock)) != rex2); \
386 splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \
387 " returned %d (expected %d) when !RW_READER\n", \
388 _rc2_ ? "Fail " : "", result, rex2); \
389 \
390 rw_enter(&(rwp)->rw_rwlock, RW_WRITER); \
391 _rc3_ = ((result = held_func(&(rwp)->rw_rwlock)) != wex1); \
392 splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \
393 " returned %d (expected %d) when RW_WRITER\n", \
394 _rc3_ ? "Fail " : "", result, wex1); \
395 rw_exit(&(rwp)->rw_rwlock); \
396 _rc4_ = ((result = held_func(&(rwp)->rw_rwlock)) != wex2); \
397 splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \
398 " returned %d (expected %d) when !RW_WRITER\n", \
399 _rc4_ ? "Fail " : "", result, wex2); \
400 \
401 rc = ((_rc1_ || _rc2_ || _rc3_ || _rc4_) ? -EINVAL : 0); \
402} while(0);
403
f1ca4da6 404static int
7c50328b 405splat_rwlock_test3(struct file *file, void *arg)
f1ca4da6 406{
e811949a
BB
407 rw_priv_t *rwp;
408 int rc1, rc2, rc3;
f1ca4da6 409
e811949a
BB
410 rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
411 if (rwp == NULL)
412 return -ENOMEM;
f1ca4da6 413
e811949a 414 splat_init_rw_priv(rwp, file);
f1ca4da6 415
e811949a
BB
416 splat_rwlock_test3_helper(rwp, 1, 0, 1, 0, RW_LOCK_HELD, rc1);
417 splat_rwlock_test3_helper(rwp, 1, 0, 0, 0, RW_READ_HELD, rc2);
418 splat_rwlock_test3_helper(rwp, 0, 0, 1, 0, RW_WRITE_HELD, rc3);
f1ca4da6 419
e811949a
BB
420 rw_destroy(&rwp->rw_rwlock);
421 kfree(rwp);
422
423 return ((rc1 || rc2 || rc3) ? -EINVAL : 0);
f1ca4da6 424}
425
e811949a
BB
426static void
427splat_rwlock_test4_func(void *arg)
f1ca4da6 428{
e811949a
BB
429 rw_priv_t *rwp = (rw_priv_t *)arg;
430 ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
f1ca4da6 431
e811949a
BB
432 if (rw_tryenter(&rwp->rw_rwlock, rwp->rw_type)) {
433 rwp->rw_rc = 0;
434 rw_exit(&rwp->rw_rwlock);
435 } else {
436 rwp->rw_rc = -EBUSY;
f1ca4da6 437 }
e811949a
BB
438}
439
440static char *
441splat_rwlock_test4_name(krw_t type)
442{
443 switch (type) {
444 case RW_NONE: return "RW_NONE";
445 case RW_WRITER: return "RW_WRITER";
446 case RW_READER: return "RW_READER";
f1ca4da6 447 }
448
e811949a 449 return NULL;
f1ca4da6 450}
451
452static int
e811949a
BB
453splat_rwlock_test4_type(taskq_t *tq, rw_priv_t *rwp, int expected_rc,
454 krw_t holder_type, krw_t try_type)
f1ca4da6 455{
e811949a 456 int id, rc = 0;
f1ca4da6 457
e811949a
BB
458 /* Schedule a task function which will try and acquire the rwlock
459 * using type try_type while the rwlock is being held as holder_type.
460 * The result must match expected_rc for the test to pass */
461 rwp->rw_rc = -EINVAL;
462 rwp->rw_type = try_type;
f1ca4da6 463
e811949a
BB
464 if (holder_type == RW_WRITER || holder_type == RW_READER)
465 rw_enter(&rwp->rw_rwlock, holder_type);
f1ca4da6 466
e811949a
BB
467 id = taskq_dispatch(tq, splat_rwlock_test4_func, rwp, TQ_SLEEP);
468 if (id == 0) {
469 splat_vprint(rwp->rw_file, SPLAT_RWLOCK_TEST4_NAME, "%s",
470 "taskq_dispatch() failed\n");
471 rc = -EINVAL;
472 goto out;
f1ca4da6 473 }
f1ca4da6 474
e811949a 475 taskq_wait_id(tq, id);
f1ca4da6 476
e811949a
BB
477 if (rwp->rw_rc != expected_rc)
478 rc = -EINVAL;
f1ca4da6 479
e811949a
BB
480 splat_vprint(rwp->rw_file, SPLAT_RWLOCK_TEST4_NAME,
481 "%srw_tryenter(%s) returned %d (expected %d) when %s\n",
482 rc ? "Fail " : "", splat_rwlock_test4_name(try_type),
483 rwp->rw_rc, expected_rc,
484 splat_rwlock_test4_name(holder_type));
485out:
486 if (holder_type == RW_WRITER || holder_type == RW_READER)
487 rw_exit(&rwp->rw_rwlock);
f1ca4da6 488
f1ca4da6 489 return rc;
490}
491
492static int
e811949a 493splat_rwlock_test4(struct file *file, void *arg)
f1ca4da6 494{
e811949a
BB
495 rw_priv_t *rwp;
496 taskq_t *tq;
497 int rc = 0, rc1, rc2, rc3, rc4, rc5, rc6;
498
499 rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
500 if (rwp == NULL)
501 return -ENOMEM;
502
62aa81a5 503 tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, 1, defclsyspri,
e811949a
BB
504 50, INT_MAX, TASKQ_PREPOPULATE);
505 if (tq == NULL) {
506 rc = -ENOMEM;
f1ca4da6 507 goto out;
508 }
509
e811949a
BB
510 splat_init_rw_priv(rwp, file);
511
512 /* Validate all combinations of rw_tryenter() contention */
513 rc1 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_WRITER, RW_WRITER);
514 rc2 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_WRITER, RW_READER);
515 rc3 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_READER, RW_WRITER);
516 rc4 = splat_rwlock_test4_type(tq, rwp, 0, RW_READER, RW_READER);
517 rc5 = splat_rwlock_test4_type(tq, rwp, 0, RW_NONE, RW_WRITER);
518 rc6 = splat_rwlock_test4_type(tq, rwp, 0, RW_NONE, RW_READER);
f1ca4da6 519
e811949a 520 if (rc1 || rc2 || rc3 || rc4 || rc5 || rc6)
f1ca4da6 521 rc = -EINVAL;
e811949a
BB
522
523 taskq_destroy(tq);
524out:
525 rw_destroy(&(rwp->rw_rwlock));
526 kfree(rwp);
527
528 return rc;
529}
530
531static int
532splat_rwlock_test5(struct file *file, void *arg)
533{
534 rw_priv_t *rwp;
535 int rc = -EINVAL;
536
537 rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
538 if (rwp == NULL)
539 return -ENOMEM;
540
541 splat_init_rw_priv(rwp, file);
542
543 rw_enter(&rwp->rw_rwlock, RW_WRITER);
544 if (!RW_WRITE_HELD(&rwp->rw_rwlock)) {
545 splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME,
546 "rwlock should be write lock: %d\n",
547 RW_WRITE_HELD(&rwp->rw_rwlock));
f1ca4da6 548 goto out;
549 }
550
e811949a
BB
551 rw_downgrade(&rwp->rw_rwlock);
552 if (!RW_READ_HELD(&rwp->rw_rwlock)) {
553 splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME,
554 "rwlock should be read lock: %d\n",
555 RW_READ_HELD(&rwp->rw_rwlock));
556 goto out;
557 }
f1ca4da6 558
e811949a
BB
559 rc = 0;
560 splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "%s",
561 "rwlock properly downgraded\n");
f1ca4da6 562out:
e811949a
BB
563 rw_exit(&rwp->rw_rwlock);
564 rw_destroy(&rwp->rw_rwlock);
565 kfree(rwp);
566
f1ca4da6 567 return rc;
568}
569
570static int
7c50328b 571splat_rwlock_test6(struct file *file, void *arg)
f1ca4da6 572{
e811949a 573 rw_priv_t *rwp;
32f5faff 574 int rc;
e811949a
BB
575
576 rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
577 if (rwp == NULL)
578 return -ENOMEM;
579
580 splat_init_rw_priv(rwp, file);
581
582 rw_enter(&rwp->rw_rwlock, RW_READER);
583 if (!RW_READ_HELD(&rwp->rw_rwlock)) {
584 splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME,
585 "rwlock should be read lock: %d\n",
586 RW_READ_HELD(&rwp->rw_rwlock));
32f5faff 587 rc = -ENOLCK;
f1ca4da6 588 goto out;
589 }
590
32f5faff
BB
591#if defined(CONFIG_RWSEM_GENERIC_SPINLOCK)
592 /* With one reader upgrade should never fail. */
e811949a
BB
593 rc = rw_tryupgrade(&rwp->rw_rwlock);
594 if (!rc) {
595 splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME,
32f5faff
BB
596 "rwlock failed upgrade from reader: %d\n",
597 RW_READ_HELD(&rwp->rw_rwlock));
598 rc = -ENOLCK;
e811949a
BB
599 goto out;
600 }
f1ca4da6 601
e811949a 602 if (RW_READ_HELD(&rwp->rw_rwlock) || !RW_WRITE_HELD(&rwp->rw_rwlock)) {
7c50328b 603 splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "rwlock should "
e811949a
BB
604 "have 0 (not %d) reader and 1 (not %d) writer\n",
605 RW_READ_HELD(&rwp->rw_rwlock),
606 RW_WRITE_HELD(&rwp->rw_rwlock));
f1ca4da6 607 goto out;
608 }
609
e811949a
BB
610 rc = 0;
611 splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "%s",
612 "rwlock properly upgraded\n");
32f5faff
BB
613#else
614 rc = 0;
615 splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "%s",
616 "rw_tryupgrade() is disabled for this arch\n");
617#endif
f1ca4da6 618out:
e811949a
BB
619 rw_exit(&rwp->rw_rwlock);
620 rw_destroy(&rwp->rw_rwlock);
621 kfree(rwp);
622
f1ca4da6 623 return rc;
624}
625
7c50328b 626splat_subsystem_t *
627splat_rwlock_init(void)
f1ca4da6 628{
e811949a
BB
629 splat_subsystem_t *sub;
630
631 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
632 if (sub == NULL)
633 return NULL;
634
635 memset(sub, 0, sizeof(*sub));
636 strncpy(sub->desc.name, SPLAT_RWLOCK_NAME, SPLAT_NAME_SIZE);
637 strncpy(sub->desc.desc, SPLAT_RWLOCK_DESC, SPLAT_DESC_SIZE);
638 INIT_LIST_HEAD(&sub->subsystem_list);
639 INIT_LIST_HEAD(&sub->test_list);
640 spin_lock_init(&sub->test_lock);
641 sub->desc.id = SPLAT_SUBSYSTEM_RWLOCK;
642
643 SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST1_NAME, SPLAT_RWLOCK_TEST1_DESC,
644 SPLAT_RWLOCK_TEST1_ID, splat_rwlock_test1);
645 SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST2_NAME, SPLAT_RWLOCK_TEST2_DESC,
646 SPLAT_RWLOCK_TEST2_ID, splat_rwlock_test2);
647 SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST3_NAME, SPLAT_RWLOCK_TEST3_DESC,
648 SPLAT_RWLOCK_TEST3_ID, splat_rwlock_test3);
649 SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST4_NAME, SPLAT_RWLOCK_TEST4_DESC,
650 SPLAT_RWLOCK_TEST4_ID, splat_rwlock_test4);
651 SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST5_NAME, SPLAT_RWLOCK_TEST5_DESC,
652 SPLAT_RWLOCK_TEST5_ID, splat_rwlock_test5);
653 SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST6_NAME, SPLAT_RWLOCK_TEST6_DESC,
654 SPLAT_RWLOCK_TEST6_ID, splat_rwlock_test6);
655
656 return sub;
f1ca4da6 657}
658
659void
7c50328b 660splat_rwlock_fini(splat_subsystem_t *sub)
f1ca4da6 661{
e811949a
BB
662 ASSERT(sub);
663 SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST6_ID);
664 SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST5_ID);
665 SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST4_ID);
666 SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST3_ID);
667 SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST2_ID);
668 SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST1_ID);
669 kfree(sub);
f1ca4da6 670}
671
672int
7c50328b 673splat_rwlock_id(void) {
e811949a 674 return SPLAT_SUBSYSTEM_RWLOCK;
f1ca4da6 675}