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