]>
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++) { | |
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)); | |
373 | out: | |
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) \ |
379 | do { \ | |
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 | 408 | static int |
7c50328b | 409 | splat_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 |
430 | static void |
431 | splat_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 | ||
444 | static char * | |
445 | splat_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 | ||
456 | static int | |
e811949a BB |
457 | splat_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)); | |
489 | out: | |
490 | if (holder_type == RW_WRITER || holder_type == RW_READER) | |
491 | rw_exit(&rwp->rw_rwlock); | |
f1ca4da6 | 492 | |
f1ca4da6 | 493 | return rc; |
494 | } | |
495 | ||
496 | static int | |
e811949a | 497 | splat_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); | |
528 | out: | |
529 | rw_destroy(&(rwp->rw_rwlock)); | |
530 | kfree(rwp); | |
531 | ||
532 | return rc; | |
533 | } | |
534 | ||
535 | static int | |
536 | splat_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 | 566 | out: |
e811949a BB |
567 | rw_exit(&rwp->rw_rwlock); |
568 | rw_destroy(&rwp->rw_rwlock); | |
569 | kfree(rwp); | |
570 | ||
f1ca4da6 | 571 | return rc; |
572 | } | |
573 | ||
574 | static int | |
7c50328b | 575 | splat_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"); | |
613 | out: | |
614 | rw_exit(&rwp->rw_rwlock); | |
615 | rw_destroy(&rwp->rw_rwlock); | |
616 | kfree(rwp); | |
617 | ||
618 | return rc; | |
619 | } | |
620 | ||
621 | static int | |
622 | splat_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 | 663 | out: |
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 | 671 | splat_subsystem_t * |
672 | splat_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 | ||
706 | void | |
7c50328b | 707 | splat_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 | ||
720 | int | |
7c50328b | 721 | splat_rwlock_id(void) { |
e811949a | 722 | return SPLAT_SUBSYSTEM_RWLOCK; |
f1ca4da6 | 723 | } |