]>
Commit | Line | Data |
---|---|---|
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 | |
10946b02 | 27 | #include <sys/random.h> |
df870a69 BB |
28 | #include <sys/rwlock.h> |
29 | #include <sys/taskq.h> | |
10946b02 AX |
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 | |
70 | typedef 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 | ||
84 | typedef 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 | 90 | void 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 |
105 | static int |
106 | splat_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 |
150 | static int |
151 | splat_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 | ||
199 | static int | |
7c50328b | 200 | splat_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) | |
9e4fb5c2 | 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 |
9e4fb5c2 | 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 |
297 | static void |
298 | splat_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 | ||
315 | static int | |
7c50328b | 316 | splat_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(), | |
330 | maxclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE); | |
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)); | |
369 | out: | |
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) \ |
375 | do { \ | |
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 | 404 | static int |
7c50328b | 405 | splat_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 |
426 | static void |
427 | splat_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 | ||
440 | static char * | |
441 | splat_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 | ||
452 | static int | |
e811949a BB |
453 | splat_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)); | |
485 | out: | |
486 | if (holder_type == RW_WRITER || holder_type == RW_READER) | |
487 | rw_exit(&rwp->rw_rwlock); | |
f1ca4da6 | 488 | |
f1ca4da6 | 489 | return rc; |
490 | } | |
491 | ||
492 | static int | |
e811949a | 493 | splat_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 | ||
503 | tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, 1, maxclsyspri, | |
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); | |
524 | out: | |
525 | rw_destroy(&(rwp->rw_rwlock)); | |
526 | kfree(rwp); | |
527 | ||
528 | return rc; | |
529 | } | |
530 | ||
531 | static int | |
532 | splat_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 | 562 | out: |
e811949a BB |
563 | rw_exit(&rwp->rw_rwlock); |
564 | rw_destroy(&rwp->rw_rwlock); | |
565 | kfree(rwp); | |
566 | ||
f1ca4da6 | 567 | return rc; |
568 | } | |
569 | ||
570 | static int | |
7c50328b | 571 | splat_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 | 618 | out: |
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 | 626 | splat_subsystem_t * |
627 | splat_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 | ||
659 | void | |
7c50328b | 660 | splat_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 | ||
672 | int | |
7c50328b | 673 | splat_rwlock_id(void) { |
e811949a | 674 | return SPLAT_SUBSYSTEM_RWLOCK; |
f1ca4da6 | 675 | } |