]>
Commit | Line | Data |
---|---|---|
715f6251 | 1 | /* |
2 | * This file is part of the SPL: Solaris Porting Layer. | |
3 | * | |
4 | * Copyright (c) 2008 Lawrence Livermore National Security, LLC. | |
5 | * Produced at Lawrence Livermore National Laboratory | |
6 | * Written by: | |
7 | * Brian Behlendorf <behlendorf1@llnl.gov>, | |
8 | * Herb Wartens <wartens2@llnl.gov>, | |
9 | * Jim Garlick <garlick@llnl.gov> | |
10 | * UCRL-CODE-235197 | |
11 | * | |
12 | * This is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * This is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License along | |
23 | * with this program; if not, write to the Free Software Foundation, Inc., | |
24 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
25 | */ | |
26 | ||
7c50328b | 27 | #include "splat-internal.h" |
f1ca4da6 | 28 | |
7c50328b | 29 | #define SPLAT_SUBSYSTEM_MUTEX 0x0400 |
30 | #define SPLAT_MUTEX_NAME "mutex" | |
31 | #define SPLAT_MUTEX_DESC "Kernel Mutex Tests" | |
f1ca4da6 | 32 | |
7c50328b | 33 | #define SPLAT_MUTEX_TEST1_ID 0x0401 |
34 | #define SPLAT_MUTEX_TEST1_NAME "tryenter" | |
35 | #define SPLAT_MUTEX_TEST1_DESC "Validate mutex_tryenter() correctness" | |
f1ca4da6 | 36 | |
7c50328b | 37 | #define SPLAT_MUTEX_TEST2_ID 0x0402 |
38 | #define SPLAT_MUTEX_TEST2_NAME "race" | |
39 | #define SPLAT_MUTEX_TEST2_DESC "Many threads entering/exiting the mutex" | |
f1ca4da6 | 40 | |
7c50328b | 41 | #define SPLAT_MUTEX_TEST3_ID 0x0403 |
42 | #define SPLAT_MUTEX_TEST3_NAME "owned" | |
43 | #define SPLAT_MUTEX_TEST3_DESC "Validate mutex_owned() correctness" | |
f1ca4da6 | 44 | |
7c50328b | 45 | #define SPLAT_MUTEX_TEST4_ID 0x0404 |
46 | #define SPLAT_MUTEX_TEST4_NAME "owner" | |
47 | #define SPLAT_MUTEX_TEST4_DESC "Validate mutex_owner() correctness" | |
f1ca4da6 | 48 | |
7c50328b | 49 | #define SPLAT_MUTEX_TEST_MAGIC 0x115599DDUL |
50 | #define SPLAT_MUTEX_TEST_NAME "mutex_test" | |
51 | #define SPLAT_MUTEX_TEST_WORKQ "mutex_wq" | |
52 | #define SPLAT_MUTEX_TEST_COUNT 128 | |
f1ca4da6 | 53 | |
54 | typedef struct mutex_priv { | |
55 | unsigned long mp_magic; | |
56 | struct file *mp_file; | |
7c50328b | 57 | struct work_struct mp_work[SPLAT_MUTEX_TEST_COUNT]; |
f1ca4da6 | 58 | kmutex_t mp_mtx; |
59 | int mp_rc; | |
60 | } mutex_priv_t; | |
61 | ||
57d86234 | 62 | #ifdef HAVE_3ARGS_INIT_WORK |
f1ca4da6 | 63 | static void |
7c50328b | 64 | splat_mutex_test1_work(void *priv) |
f1ca4da6 | 65 | { |
66 | mutex_priv_t *mp = (mutex_priv_t *)priv; | |
67 | ||
7c50328b | 68 | ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC); |
f1ca4da6 | 69 | mp->mp_rc = 0; |
70 | ||
71 | if (!mutex_tryenter(&mp->mp_mtx)) | |
72 | mp->mp_rc = -EBUSY; | |
73 | } | |
57d86234 | 74 | #endif |
f1ca4da6 | 75 | |
76 | static int | |
7c50328b | 77 | splat_mutex_test1(struct file *file, void *arg) |
f1ca4da6 | 78 | { |
57d86234 | 79 | int rc = 0; |
80 | #ifdef HAVE_3ARGS_INIT_WORK | |
f1ca4da6 | 81 | struct workqueue_struct *wq; |
82 | struct work_struct work; | |
83 | mutex_priv_t *mp; | |
f1ca4da6 | 84 | |
85 | mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL); | |
86 | if (mp == NULL) | |
87 | return -ENOMEM; | |
88 | ||
7c50328b | 89 | wq = create_singlethread_workqueue(SPLAT_MUTEX_TEST_WORKQ); |
f1ca4da6 | 90 | if (wq == NULL) { |
91 | rc = -ENOMEM; | |
92 | goto out2; | |
93 | } | |
94 | ||
7c50328b | 95 | mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL); |
f1ca4da6 | 96 | mutex_enter(&(mp->mp_mtx)); |
97 | ||
7c50328b | 98 | mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC; |
f1ca4da6 | 99 | mp->mp_file = file; |
7c50328b | 100 | INIT_WORK(&work, splat_mutex_test1_work, mp); |
f1ca4da6 | 101 | |
102 | /* Schedule a work item which will try and aquire the mutex via | |
103 | * mutex_tryenter() while its held. This should fail and the work | |
104 | * item will indicte this status in the passed private data. */ | |
105 | if (!queue_work(wq, &work)) { | |
106 | mutex_exit(&(mp->mp_mtx)); | |
107 | rc = -EINVAL; | |
108 | goto out; | |
109 | } | |
110 | ||
111 | flush_workqueue(wq); | |
112 | mutex_exit(&(mp->mp_mtx)); | |
113 | ||
114 | /* Work item successfully aquired mutex, very bad! */ | |
115 | if (mp->mp_rc != -EBUSY) { | |
116 | rc = -EINVAL; | |
117 | goto out; | |
118 | } | |
119 | ||
7c50328b | 120 | splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s", |
f1ca4da6 | 121 | "mutex_trylock() correctly failed when mutex held\n"); |
122 | ||
123 | /* Schedule a work item which will try and aquire the mutex via | |
124 | * mutex_tryenter() while it is not held. This should work and | |
125 | * the item will indicte this status in the passed private data. */ | |
126 | if (!queue_work(wq, &work)) { | |
127 | rc = -EINVAL; | |
128 | goto out; | |
129 | } | |
130 | ||
131 | flush_workqueue(wq); | |
132 | ||
133 | /* Work item failed to aquire mutex, very bad! */ | |
134 | if (mp->mp_rc != 0) { | |
135 | rc = -EINVAL; | |
136 | goto out; | |
137 | } | |
138 | ||
7c50328b | 139 | splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s", |
f1ca4da6 | 140 | "mutex_trylock() correctly succeeded when mutex unheld\n"); |
141 | out: | |
142 | mutex_destroy(&(mp->mp_mtx)); | |
143 | destroy_workqueue(wq); | |
144 | out2: | |
145 | kfree(mp); | |
57d86234 | 146 | #endif |
f1ca4da6 | 147 | return rc; |
148 | } | |
149 | ||
57d86234 | 150 | #ifdef HAVE_3ARGS_INIT_WORK |
f1ca4da6 | 151 | static void |
7c50328b | 152 | splat_mutex_test2_work(void *priv) |
f1ca4da6 | 153 | { |
154 | mutex_priv_t *mp = (mutex_priv_t *)priv; | |
155 | int rc; | |
156 | ||
7c50328b | 157 | ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC); |
f1ca4da6 | 158 | |
159 | /* Read the value before sleeping and write it after we wake up to | |
160 | * maximize the chance of a race if mutexs are not working properly */ | |
161 | mutex_enter(&mp->mp_mtx); | |
162 | rc = mp->mp_rc; | |
163 | set_current_state(TASK_INTERRUPTIBLE); | |
164 | schedule_timeout(HZ / 100); /* 1/100 of a second */ | |
165 | mp->mp_rc = rc + 1; | |
166 | mutex_exit(&mp->mp_mtx); | |
167 | } | |
57d86234 | 168 | #endif |
f1ca4da6 | 169 | |
170 | static int | |
7c50328b | 171 | splat_mutex_test2(struct file *file, void *arg) |
f1ca4da6 | 172 | { |
57d86234 | 173 | int rc = 0; |
174 | #ifdef HAVE_3ARGS_INIT_WORK | |
f1ca4da6 | 175 | struct workqueue_struct *wq; |
176 | mutex_priv_t *mp; | |
57d86234 | 177 | int i; |
f1ca4da6 | 178 | |
179 | mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL); | |
180 | if (mp == NULL) | |
181 | return -ENOMEM; | |
182 | ||
183 | /* Create a thread per CPU items on queue will race */ | |
7c50328b | 184 | wq = create_workqueue(SPLAT_MUTEX_TEST_WORKQ); |
f1ca4da6 | 185 | if (wq == NULL) { |
186 | rc = -ENOMEM; | |
187 | goto out; | |
188 | } | |
189 | ||
7c50328b | 190 | mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL); |
f1ca4da6 | 191 | |
7c50328b | 192 | mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC; |
f1ca4da6 | 193 | mp->mp_file = file; |
194 | mp->mp_rc = 0; | |
195 | ||
196 | /* Schedule N work items to the work queue each of which enters the | |
197 | * mutex, sleeps briefly, then exits the mutex. On a multiprocessor | |
198 | * box these work items will be handled by all available CPUs. The | |
199 | * mutex is instrumented such that if any two processors are in the | |
200 | * critical region at the same time the system will panic. If the | |
201 | * mutex is implemented right this will never happy, that's a pass. */ | |
7c50328b | 202 | for (i = 0; i < SPLAT_MUTEX_TEST_COUNT; i++) { |
203 | INIT_WORK(&(mp->mp_work[i]), splat_mutex_test2_work, mp); | |
f1ca4da6 | 204 | |
205 | if (!queue_work(wq, &(mp->mp_work[i]))) { | |
7c50328b | 206 | splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, |
f1ca4da6 | 207 | "Failed to queue work id %d\n", i); |
208 | rc = -EINVAL; | |
209 | } | |
210 | } | |
211 | ||
212 | flush_workqueue(wq); | |
213 | ||
7c50328b | 214 | if (mp->mp_rc == SPLAT_MUTEX_TEST_COUNT) { |
215 | splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads " | |
f1ca4da6 | 216 | "correctly entered/exited the mutex %d times\n", |
217 | num_online_cpus(), mp->mp_rc); | |
218 | } else { | |
7c50328b | 219 | splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads " |
f1ca4da6 | 220 | "only processed %d/%d mutex work items\n", |
7c50328b | 221 | num_online_cpus(), mp->mp_rc, SPLAT_MUTEX_TEST_COUNT); |
f1ca4da6 | 222 | rc = -EINVAL; |
223 | } | |
224 | ||
225 | mutex_destroy(&(mp->mp_mtx)); | |
226 | destroy_workqueue(wq); | |
227 | out: | |
228 | kfree(mp); | |
57d86234 | 229 | #endif |
f1ca4da6 | 230 | return rc; |
231 | } | |
232 | ||
233 | static int | |
7c50328b | 234 | splat_mutex_test3(struct file *file, void *arg) |
f1ca4da6 | 235 | { |
236 | kmutex_t mtx; | |
237 | int rc = 0; | |
238 | ||
7c50328b | 239 | mutex_init(&mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL); |
f1ca4da6 | 240 | |
241 | mutex_enter(&mtx); | |
242 | ||
243 | /* Mutex should be owned by current */ | |
244 | if (!mutex_owned(&mtx)) { | |
7c50328b | 245 | splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should " |
f1ca4da6 | 246 | "be owned by pid %d but is owned by pid %d\n", |
247 | current->pid, mtx.km_owner ? mtx.km_owner->pid : -1); | |
248 | rc = -EINVAL; | |
249 | goto out; | |
250 | } | |
251 | ||
252 | mutex_exit(&mtx); | |
253 | ||
254 | /* Mutex should not be owned by any task */ | |
255 | if (mutex_owned(&mtx)) { | |
7c50328b | 256 | splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should " |
f1ca4da6 | 257 | "not be owned but is owned by pid %d\n", |
258 | mtx.km_owner ? mtx.km_owner->pid : -1); | |
259 | rc = -EINVAL; | |
260 | goto out; | |
261 | } | |
262 | ||
7c50328b | 263 | splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s", |
f1ca4da6 | 264 | "Correct mutex_owned() behavior\n"); |
265 | out: | |
266 | mutex_destroy(&mtx); | |
267 | ||
268 | return rc; | |
269 | } | |
270 | ||
271 | static int | |
7c50328b | 272 | splat_mutex_test4(struct file *file, void *arg) |
f1ca4da6 | 273 | { |
274 | kmutex_t mtx; | |
275 | kthread_t *owner; | |
276 | int rc = 0; | |
277 | ||
7c50328b | 278 | mutex_init(&mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL); |
f1ca4da6 | 279 | |
280 | mutex_enter(&mtx); | |
281 | ||
282 | /* Mutex should be owned by current */ | |
283 | owner = mutex_owner(&mtx); | |
284 | if (current != owner) { | |
7c50328b | 285 | splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should " |
f1ca4da6 | 286 | "be owned by pid %d but is owned by pid %d\n", |
287 | current->pid, owner ? owner->pid : -1); | |
288 | rc = -EINVAL; | |
289 | goto out; | |
290 | } | |
291 | ||
292 | mutex_exit(&mtx); | |
293 | ||
294 | /* Mutex should not be owned by any task */ | |
295 | owner = mutex_owner(&mtx); | |
296 | if (owner) { | |
7c50328b | 297 | splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should not " |
f1ca4da6 | 298 | "be owned but is owned by pid %d\n", owner->pid); |
299 | rc = -EINVAL; | |
300 | goto out; | |
301 | } | |
302 | ||
7c50328b | 303 | splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s", |
f1ca4da6 | 304 | "Correct mutex_owner() behavior\n"); |
305 | out: | |
306 | mutex_destroy(&mtx); | |
307 | ||
308 | return rc; | |
309 | } | |
310 | ||
7c50328b | 311 | splat_subsystem_t * |
312 | splat_mutex_init(void) | |
f1ca4da6 | 313 | { |
7c50328b | 314 | splat_subsystem_t *sub; |
f1ca4da6 | 315 | |
316 | sub = kmalloc(sizeof(*sub), GFP_KERNEL); | |
317 | if (sub == NULL) | |
318 | return NULL; | |
319 | ||
320 | memset(sub, 0, sizeof(*sub)); | |
7c50328b | 321 | strncpy(sub->desc.name, SPLAT_MUTEX_NAME, SPLAT_NAME_SIZE); |
322 | strncpy(sub->desc.desc, SPLAT_MUTEX_DESC, SPLAT_DESC_SIZE); | |
f1ca4da6 | 323 | INIT_LIST_HEAD(&sub->subsystem_list); |
324 | INIT_LIST_HEAD(&sub->test_list); | |
325 | spin_lock_init(&sub->test_lock); | |
7c50328b | 326 | sub->desc.id = SPLAT_SUBSYSTEM_MUTEX; |
f1ca4da6 | 327 | |
7c50328b | 328 | SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST1_NAME, SPLAT_MUTEX_TEST1_DESC, |
329 | SPLAT_MUTEX_TEST1_ID, splat_mutex_test1); | |
330 | SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST2_NAME, SPLAT_MUTEX_TEST2_DESC, | |
331 | SPLAT_MUTEX_TEST2_ID, splat_mutex_test2); | |
332 | SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST3_NAME, SPLAT_MUTEX_TEST3_DESC, | |
333 | SPLAT_MUTEX_TEST3_ID, splat_mutex_test3); | |
334 | SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST4_NAME, SPLAT_MUTEX_TEST4_DESC, | |
335 | SPLAT_MUTEX_TEST4_ID, splat_mutex_test4); | |
f1ca4da6 | 336 | |
337 | return sub; | |
338 | } | |
339 | ||
340 | void | |
7c50328b | 341 | splat_mutex_fini(splat_subsystem_t *sub) |
f1ca4da6 | 342 | { |
343 | ASSERT(sub); | |
7c50328b | 344 | SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST4_ID); |
345 | SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST3_ID); | |
346 | SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST2_ID); | |
347 | SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST1_ID); | |
f1ca4da6 | 348 | |
349 | kfree(sub); | |
350 | } | |
351 | ||
352 | int | |
7c50328b | 353 | splat_mutex_id(void) { |
354 | return SPLAT_SUBSYSTEM_MUTEX; | |
f1ca4da6 | 355 | } |