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