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