]>
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 | |
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 | |
74 | typedef 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 | ||
88 | typedef 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 | 94 | void 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 |
109 | static int |
110 | splat_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 |
154 | static int |
155 | splat_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 | ||
203 | static int | |
7c50328b | 204 | splat_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 |
301 | static void |
302 | splat_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 | ||
319 | static int | |
7c50328b | 320 | splat_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++) { | |
cbba7146 U |
351 | if (taskq_dispatch(tq, splat_rwlock_test2_func, rwp, |
352 | TQ_SLEEP) == TASKQID_INVALID) { | |
e811949a BB |
353 | splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, |
354 | "Failed to queue task %d\n", i); | |
355 | rc = -EINVAL; | |
356 | } | |
f1ca4da6 | 357 | } |
f1ca4da6 | 358 | |
e811949a | 359 | taskq_wait(tq); |
f1ca4da6 | 360 | |
e811949a BB |
361 | if (rwp->rw_rc == tq_count) { |
362 | splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads " | |
363 | "correctly entered/exited the rwlock %d times\n", | |
364 | num_online_cpus(), rwp->rw_rc); | |
365 | } else { | |
366 | splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads " | |
367 | "only processed %d/%d w rwlock work items\n", | |
368 | num_online_cpus(), rwp->rw_rc, tq_count); | |
369 | rc = -EINVAL; | |
f1ca4da6 | 370 | } |
371 | ||
e811949a BB |
372 | taskq_destroy(tq); |
373 | rw_destroy(&(rwp->rw_rwlock)); | |
374 | out: | |
375 | kfree(rwp); | |
f1ca4da6 | 376 | return rc; |
377 | } | |
378 | ||
e811949a BB |
379 | #define splat_rwlock_test3_helper(rwp,rex1,rex2,wex1,wex2,held_func,rc) \ |
380 | do { \ | |
381 | int result, _rc1_, _rc2_, _rc3_, _rc4_; \ | |
382 | \ | |
383 | rc = 0; \ | |
384 | rw_enter(&(rwp)->rw_rwlock, RW_READER); \ | |
385 | _rc1_ = ((result = held_func(&(rwp)->rw_rwlock)) != rex1); \ | |
386 | splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \ | |
387 | " returned %d (expected %d) when RW_READER\n", \ | |
388 | _rc1_ ? "Fail " : "", result, rex1); \ | |
389 | rw_exit(&(rwp)->rw_rwlock); \ | |
390 | _rc2_ = ((result = held_func(&(rwp)->rw_rwlock)) != rex2); \ | |
391 | splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \ | |
392 | " returned %d (expected %d) when !RW_READER\n", \ | |
393 | _rc2_ ? "Fail " : "", result, rex2); \ | |
394 | \ | |
395 | rw_enter(&(rwp)->rw_rwlock, RW_WRITER); \ | |
396 | _rc3_ = ((result = held_func(&(rwp)->rw_rwlock)) != wex1); \ | |
397 | splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \ | |
398 | " returned %d (expected %d) when RW_WRITER\n", \ | |
399 | _rc3_ ? "Fail " : "", result, wex1); \ | |
400 | rw_exit(&(rwp)->rw_rwlock); \ | |
401 | _rc4_ = ((result = held_func(&(rwp)->rw_rwlock)) != wex2); \ | |
402 | splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \ | |
403 | " returned %d (expected %d) when !RW_WRITER\n", \ | |
404 | _rc4_ ? "Fail " : "", result, wex2); \ | |
405 | \ | |
406 | rc = ((_rc1_ || _rc2_ || _rc3_ || _rc4_) ? -EINVAL : 0); \ | |
407 | } while(0); | |
408 | ||
f1ca4da6 | 409 | static int |
7c50328b | 410 | splat_rwlock_test3(struct file *file, void *arg) |
f1ca4da6 | 411 | { |
e811949a BB |
412 | rw_priv_t *rwp; |
413 | int rc1, rc2, rc3; | |
f1ca4da6 | 414 | |
e811949a BB |
415 | rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); |
416 | if (rwp == NULL) | |
417 | return -ENOMEM; | |
f1ca4da6 | 418 | |
e811949a | 419 | splat_init_rw_priv(rwp, file); |
f1ca4da6 | 420 | |
e811949a BB |
421 | splat_rwlock_test3_helper(rwp, 1, 0, 1, 0, RW_LOCK_HELD, rc1); |
422 | splat_rwlock_test3_helper(rwp, 1, 0, 0, 0, RW_READ_HELD, rc2); | |
423 | splat_rwlock_test3_helper(rwp, 0, 0, 1, 0, RW_WRITE_HELD, rc3); | |
f1ca4da6 | 424 | |
e811949a BB |
425 | rw_destroy(&rwp->rw_rwlock); |
426 | kfree(rwp); | |
427 | ||
428 | return ((rc1 || rc2 || rc3) ? -EINVAL : 0); | |
f1ca4da6 | 429 | } |
430 | ||
e811949a BB |
431 | static void |
432 | splat_rwlock_test4_func(void *arg) | |
f1ca4da6 | 433 | { |
e811949a BB |
434 | rw_priv_t *rwp = (rw_priv_t *)arg; |
435 | ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC); | |
f1ca4da6 | 436 | |
e811949a BB |
437 | if (rw_tryenter(&rwp->rw_rwlock, rwp->rw_type)) { |
438 | rwp->rw_rc = 0; | |
439 | rw_exit(&rwp->rw_rwlock); | |
440 | } else { | |
441 | rwp->rw_rc = -EBUSY; | |
f1ca4da6 | 442 | } |
e811949a BB |
443 | } |
444 | ||
445 | static char * | |
446 | splat_rwlock_test4_name(krw_t type) | |
447 | { | |
448 | switch (type) { | |
449 | case RW_NONE: return "RW_NONE"; | |
450 | case RW_WRITER: return "RW_WRITER"; | |
451 | case RW_READER: return "RW_READER"; | |
f1ca4da6 | 452 | } |
453 | ||
e811949a | 454 | return NULL; |
f1ca4da6 | 455 | } |
456 | ||
457 | static int | |
e811949a BB |
458 | splat_rwlock_test4_type(taskq_t *tq, rw_priv_t *rwp, int expected_rc, |
459 | krw_t holder_type, krw_t try_type) | |
f1ca4da6 | 460 | { |
e811949a | 461 | int id, rc = 0; |
f1ca4da6 | 462 | |
e811949a BB |
463 | /* Schedule a task function which will try and acquire the rwlock |
464 | * using type try_type while the rwlock is being held as holder_type. | |
465 | * The result must match expected_rc for the test to pass */ | |
466 | rwp->rw_rc = -EINVAL; | |
467 | rwp->rw_type = try_type; | |
f1ca4da6 | 468 | |
e811949a BB |
469 | if (holder_type == RW_WRITER || holder_type == RW_READER) |
470 | rw_enter(&rwp->rw_rwlock, holder_type); | |
f1ca4da6 | 471 | |
e811949a | 472 | id = taskq_dispatch(tq, splat_rwlock_test4_func, rwp, TQ_SLEEP); |
cbba7146 | 473 | if (id == TASKQID_INVALID) { |
e811949a BB |
474 | splat_vprint(rwp->rw_file, SPLAT_RWLOCK_TEST4_NAME, "%s", |
475 | "taskq_dispatch() failed\n"); | |
476 | rc = -EINVAL; | |
477 | goto out; | |
f1ca4da6 | 478 | } |
f1ca4da6 | 479 | |
e811949a | 480 | taskq_wait_id(tq, id); |
f1ca4da6 | 481 | |
e811949a BB |
482 | if (rwp->rw_rc != expected_rc) |
483 | rc = -EINVAL; | |
f1ca4da6 | 484 | |
e811949a BB |
485 | splat_vprint(rwp->rw_file, SPLAT_RWLOCK_TEST4_NAME, |
486 | "%srw_tryenter(%s) returned %d (expected %d) when %s\n", | |
487 | rc ? "Fail " : "", splat_rwlock_test4_name(try_type), | |
488 | rwp->rw_rc, expected_rc, | |
489 | splat_rwlock_test4_name(holder_type)); | |
490 | out: | |
491 | if (holder_type == RW_WRITER || holder_type == RW_READER) | |
492 | rw_exit(&rwp->rw_rwlock); | |
f1ca4da6 | 493 | |
f1ca4da6 | 494 | return rc; |
495 | } | |
496 | ||
497 | static int | |
e811949a | 498 | splat_rwlock_test4(struct file *file, void *arg) |
f1ca4da6 | 499 | { |
e811949a BB |
500 | rw_priv_t *rwp; |
501 | taskq_t *tq; | |
502 | int rc = 0, rc1, rc2, rc3, rc4, rc5, rc6; | |
503 | ||
504 | rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); | |
505 | if (rwp == NULL) | |
506 | return -ENOMEM; | |
507 | ||
62aa81a5 | 508 | tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, 1, defclsyspri, |
e811949a BB |
509 | 50, INT_MAX, TASKQ_PREPOPULATE); |
510 | if (tq == NULL) { | |
511 | rc = -ENOMEM; | |
f1ca4da6 | 512 | goto out; |
513 | } | |
514 | ||
e811949a BB |
515 | splat_init_rw_priv(rwp, file); |
516 | ||
517 | /* Validate all combinations of rw_tryenter() contention */ | |
518 | rc1 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_WRITER, RW_WRITER); | |
519 | rc2 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_WRITER, RW_READER); | |
520 | rc3 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_READER, RW_WRITER); | |
521 | rc4 = splat_rwlock_test4_type(tq, rwp, 0, RW_READER, RW_READER); | |
522 | rc5 = splat_rwlock_test4_type(tq, rwp, 0, RW_NONE, RW_WRITER); | |
523 | rc6 = splat_rwlock_test4_type(tq, rwp, 0, RW_NONE, RW_READER); | |
f1ca4da6 | 524 | |
e811949a | 525 | if (rc1 || rc2 || rc3 || rc4 || rc5 || rc6) |
f1ca4da6 | 526 | rc = -EINVAL; |
e811949a BB |
527 | |
528 | taskq_destroy(tq); | |
529 | out: | |
530 | rw_destroy(&(rwp->rw_rwlock)); | |
531 | kfree(rwp); | |
532 | ||
533 | return rc; | |
534 | } | |
535 | ||
536 | static int | |
537 | splat_rwlock_test5(struct file *file, void *arg) | |
538 | { | |
539 | rw_priv_t *rwp; | |
540 | int rc = -EINVAL; | |
541 | ||
542 | rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); | |
543 | if (rwp == NULL) | |
544 | return -ENOMEM; | |
545 | ||
546 | splat_init_rw_priv(rwp, file); | |
547 | ||
548 | rw_enter(&rwp->rw_rwlock, RW_WRITER); | |
549 | if (!RW_WRITE_HELD(&rwp->rw_rwlock)) { | |
550 | splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, | |
551 | "rwlock should be write lock: %d\n", | |
552 | RW_WRITE_HELD(&rwp->rw_rwlock)); | |
f1ca4da6 | 553 | goto out; |
554 | } | |
555 | ||
e811949a BB |
556 | rw_downgrade(&rwp->rw_rwlock); |
557 | if (!RW_READ_HELD(&rwp->rw_rwlock)) { | |
558 | splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, | |
559 | "rwlock should be read lock: %d\n", | |
560 | RW_READ_HELD(&rwp->rw_rwlock)); | |
561 | goto out; | |
562 | } | |
f1ca4da6 | 563 | |
e811949a BB |
564 | rc = 0; |
565 | splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "%s", | |
566 | "rwlock properly downgraded\n"); | |
f1ca4da6 | 567 | out: |
e811949a BB |
568 | rw_exit(&rwp->rw_rwlock); |
569 | rw_destroy(&rwp->rw_rwlock); | |
570 | kfree(rwp); | |
571 | ||
f1ca4da6 | 572 | return rc; |
573 | } | |
574 | ||
575 | static int | |
7c50328b | 576 | splat_rwlock_test6(struct file *file, void *arg) |
f1ca4da6 | 577 | { |
e811949a | 578 | rw_priv_t *rwp; |
32f5faff | 579 | int rc; |
e811949a BB |
580 | |
581 | rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); | |
582 | if (rwp == NULL) | |
583 | return -ENOMEM; | |
584 | ||
585 | splat_init_rw_priv(rwp, file); | |
586 | ||
587 | rw_enter(&rwp->rw_rwlock, RW_READER); | |
f58040c0 CC |
588 | if (RWSEM_COUNT(SEM(&rwp->rw_rwlock)) != |
589 | SPL_RWSEM_SINGLE_READER_VALUE) { | |
590 | splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, | |
6c2a66bf BB |
591 | "We assumed single reader rwsem->count " |
592 | "should be %ld, but is %ld\n", | |
593 | (long int)SPL_RWSEM_SINGLE_READER_VALUE, | |
594 | (long int)RWSEM_COUNT(SEM(&rwp->rw_rwlock))); | |
f58040c0 CC |
595 | rc = -ENOLCK; |
596 | goto out; | |
597 | } | |
598 | rw_exit(&rwp->rw_rwlock); | |
599 | ||
600 | rw_enter(&rwp->rw_rwlock, RW_WRITER); | |
601 | if (RWSEM_COUNT(SEM(&rwp->rw_rwlock)) != | |
602 | SPL_RWSEM_SINGLE_WRITER_VALUE) { | |
e811949a | 603 | splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, |
6c2a66bf BB |
604 | "We assumed single writer rwsem->count " |
605 | "should be %ld, but is %ld\n", | |
606 | (long int)SPL_RWSEM_SINGLE_WRITER_VALUE, | |
607 | (long int)RWSEM_COUNT(SEM(&rwp->rw_rwlock))); | |
f58040c0 CC |
608 | rc = -ENOLCK; |
609 | goto out; | |
610 | } | |
611 | rc = 0; | |
612 | splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "%s", | |
613 | "rwsem->count same as we assumed\n"); | |
614 | out: | |
615 | rw_exit(&rwp->rw_rwlock); | |
616 | rw_destroy(&rwp->rw_rwlock); | |
617 | kfree(rwp); | |
618 | ||
619 | return rc; | |
620 | } | |
621 | ||
622 | static int | |
623 | splat_rwlock_test7(struct file *file, void *arg) | |
624 | { | |
625 | rw_priv_t *rwp; | |
626 | int rc; | |
627 | ||
628 | rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); | |
629 | if (rwp == NULL) | |
630 | return -ENOMEM; | |
631 | ||
632 | splat_init_rw_priv(rwp, file); | |
633 | ||
634 | rw_enter(&rwp->rw_rwlock, RW_READER); | |
635 | if (!RW_READ_HELD(&rwp->rw_rwlock)) { | |
636 | splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, | |
e811949a BB |
637 | "rwlock should be read lock: %d\n", |
638 | RW_READ_HELD(&rwp->rw_rwlock)); | |
32f5faff | 639 | rc = -ENOLCK; |
f1ca4da6 | 640 | goto out; |
641 | } | |
642 | ||
32f5faff | 643 | /* With one reader upgrade should never fail. */ |
e811949a BB |
644 | rc = rw_tryupgrade(&rwp->rw_rwlock); |
645 | if (!rc) { | |
f58040c0 | 646 | splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, |
32f5faff BB |
647 | "rwlock failed upgrade from reader: %d\n", |
648 | RW_READ_HELD(&rwp->rw_rwlock)); | |
649 | rc = -ENOLCK; | |
e811949a BB |
650 | goto out; |
651 | } | |
f1ca4da6 | 652 | |
e811949a | 653 | if (RW_READ_HELD(&rwp->rw_rwlock) || !RW_WRITE_HELD(&rwp->rw_rwlock)) { |
f58040c0 | 654 | splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, "rwlock should " |
e811949a BB |
655 | "have 0 (not %d) reader and 1 (not %d) writer\n", |
656 | RW_READ_HELD(&rwp->rw_rwlock), | |
657 | RW_WRITE_HELD(&rwp->rw_rwlock)); | |
f1ca4da6 | 658 | goto out; |
659 | } | |
660 | ||
e811949a | 661 | rc = 0; |
f58040c0 | 662 | splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, "%s", |
e811949a | 663 | "rwlock properly upgraded\n"); |
f1ca4da6 | 664 | out: |
e811949a BB |
665 | rw_exit(&rwp->rw_rwlock); |
666 | rw_destroy(&rwp->rw_rwlock); | |
667 | kfree(rwp); | |
668 | ||
f1ca4da6 | 669 | return rc; |
670 | } | |
671 | ||
7c50328b | 672 | splat_subsystem_t * |
673 | splat_rwlock_init(void) | |
f1ca4da6 | 674 | { |
e811949a BB |
675 | splat_subsystem_t *sub; |
676 | ||
677 | sub = kmalloc(sizeof(*sub), GFP_KERNEL); | |
678 | if (sub == NULL) | |
679 | return NULL; | |
680 | ||
681 | memset(sub, 0, sizeof(*sub)); | |
682 | strncpy(sub->desc.name, SPLAT_RWLOCK_NAME, SPLAT_NAME_SIZE); | |
683 | strncpy(sub->desc.desc, SPLAT_RWLOCK_DESC, SPLAT_DESC_SIZE); | |
684 | INIT_LIST_HEAD(&sub->subsystem_list); | |
685 | INIT_LIST_HEAD(&sub->test_list); | |
686 | spin_lock_init(&sub->test_lock); | |
687 | sub->desc.id = SPLAT_SUBSYSTEM_RWLOCK; | |
688 | ||
689 | SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST1_NAME, SPLAT_RWLOCK_TEST1_DESC, | |
690 | SPLAT_RWLOCK_TEST1_ID, splat_rwlock_test1); | |
691 | SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST2_NAME, SPLAT_RWLOCK_TEST2_DESC, | |
692 | SPLAT_RWLOCK_TEST2_ID, splat_rwlock_test2); | |
693 | SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST3_NAME, SPLAT_RWLOCK_TEST3_DESC, | |
694 | SPLAT_RWLOCK_TEST3_ID, splat_rwlock_test3); | |
695 | SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST4_NAME, SPLAT_RWLOCK_TEST4_DESC, | |
696 | SPLAT_RWLOCK_TEST4_ID, splat_rwlock_test4); | |
697 | SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST5_NAME, SPLAT_RWLOCK_TEST5_DESC, | |
698 | SPLAT_RWLOCK_TEST5_ID, splat_rwlock_test5); | |
699 | SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST6_NAME, SPLAT_RWLOCK_TEST6_DESC, | |
700 | SPLAT_RWLOCK_TEST6_ID, splat_rwlock_test6); | |
f58040c0 CC |
701 | SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST7_NAME, SPLAT_RWLOCK_TEST7_DESC, |
702 | SPLAT_RWLOCK_TEST7_ID, splat_rwlock_test7); | |
e811949a BB |
703 | |
704 | return sub; | |
f1ca4da6 | 705 | } |
706 | ||
707 | void | |
7c50328b | 708 | splat_rwlock_fini(splat_subsystem_t *sub) |
f1ca4da6 | 709 | { |
e811949a | 710 | ASSERT(sub); |
f58040c0 | 711 | SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST7_ID); |
e811949a BB |
712 | SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST6_ID); |
713 | SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST5_ID); | |
714 | SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST4_ID); | |
715 | SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST3_ID); | |
716 | SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST2_ID); | |
717 | SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST1_ID); | |
718 | kfree(sub); | |
f1ca4da6 | 719 | } |
720 | ||
721 | int | |
7c50328b | 722 | splat_rwlock_id(void) { |
e811949a | 723 | return SPLAT_SUBSYSTEM_RWLOCK; |
f1ca4da6 | 724 | } |