]>
Commit | Line | Data |
---|---|---|
f2a5fec1 CW |
1 | /* |
2 | * Module-based API test facility for ww_mutexes | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, you can access it online at | |
16 | * http://www.gnu.org/licenses/gpl-2.0.html. | |
17 | */ | |
18 | ||
19 | #include <linux/kernel.h> | |
20 | ||
21 | #include <linux/completion.h> | |
22 | #include <linux/kthread.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/ww_mutex.h> | |
25 | ||
26 | static DEFINE_WW_CLASS(ww_class); | |
27 | ||
28 | struct test_mutex { | |
29 | struct work_struct work; | |
30 | struct ww_mutex mutex; | |
31 | struct completion ready, go, done; | |
32 | unsigned int flags; | |
33 | }; | |
34 | ||
35 | #define TEST_MTX_SPIN BIT(0) | |
36 | #define TEST_MTX_TRY BIT(1) | |
37 | #define TEST_MTX_CTX BIT(2) | |
38 | #define __TEST_MTX_LAST BIT(3) | |
39 | ||
40 | static void test_mutex_work(struct work_struct *work) | |
41 | { | |
42 | struct test_mutex *mtx = container_of(work, typeof(*mtx), work); | |
43 | ||
44 | complete(&mtx->ready); | |
45 | wait_for_completion(&mtx->go); | |
46 | ||
47 | if (mtx->flags & TEST_MTX_TRY) { | |
48 | while (!ww_mutex_trylock(&mtx->mutex)) | |
49 | cpu_relax(); | |
50 | } else { | |
51 | ww_mutex_lock(&mtx->mutex, NULL); | |
52 | } | |
53 | complete(&mtx->done); | |
54 | ww_mutex_unlock(&mtx->mutex); | |
55 | } | |
56 | ||
57 | static int __test_mutex(unsigned int flags) | |
58 | { | |
59 | #define TIMEOUT (HZ / 16) | |
60 | struct test_mutex mtx; | |
61 | struct ww_acquire_ctx ctx; | |
62 | int ret; | |
63 | ||
64 | ww_mutex_init(&mtx.mutex, &ww_class); | |
65 | ww_acquire_init(&ctx, &ww_class); | |
66 | ||
67 | INIT_WORK_ONSTACK(&mtx.work, test_mutex_work); | |
68 | init_completion(&mtx.ready); | |
69 | init_completion(&mtx.go); | |
70 | init_completion(&mtx.done); | |
71 | mtx.flags = flags; | |
72 | ||
73 | schedule_work(&mtx.work); | |
74 | ||
75 | wait_for_completion(&mtx.ready); | |
76 | ww_mutex_lock(&mtx.mutex, (flags & TEST_MTX_CTX) ? &ctx : NULL); | |
77 | complete(&mtx.go); | |
78 | if (flags & TEST_MTX_SPIN) { | |
79 | unsigned long timeout = jiffies + TIMEOUT; | |
80 | ||
81 | ret = 0; | |
82 | do { | |
83 | if (completion_done(&mtx.done)) { | |
84 | ret = -EINVAL; | |
85 | break; | |
86 | } | |
87 | cpu_relax(); | |
88 | } while (time_before(jiffies, timeout)); | |
89 | } else { | |
90 | ret = wait_for_completion_timeout(&mtx.done, TIMEOUT); | |
91 | } | |
92 | ww_mutex_unlock(&mtx.mutex); | |
93 | ww_acquire_fini(&ctx); | |
94 | ||
95 | if (ret) { | |
96 | pr_err("%s(flags=%x): mutual exclusion failure\n", | |
97 | __func__, flags); | |
98 | ret = -EINVAL; | |
99 | } | |
100 | ||
101 | flush_work(&mtx.work); | |
102 | destroy_work_on_stack(&mtx.work); | |
103 | return ret; | |
104 | #undef TIMEOUT | |
105 | } | |
106 | ||
107 | static int test_mutex(void) | |
108 | { | |
109 | int ret; | |
110 | int i; | |
111 | ||
112 | for (i = 0; i < __TEST_MTX_LAST; i++) { | |
113 | ret = __test_mutex(i); | |
114 | if (ret) | |
115 | return ret; | |
116 | } | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
c22fb380 CW |
121 | static int test_aa(void) |
122 | { | |
123 | struct ww_mutex mutex; | |
124 | struct ww_acquire_ctx ctx; | |
125 | int ret; | |
126 | ||
127 | ww_mutex_init(&mutex, &ww_class); | |
128 | ww_acquire_init(&ctx, &ww_class); | |
129 | ||
130 | ww_mutex_lock(&mutex, &ctx); | |
131 | ||
132 | if (ww_mutex_trylock(&mutex)) { | |
133 | pr_err("%s: trylocked itself!\n", __func__); | |
134 | ww_mutex_unlock(&mutex); | |
135 | ret = -EINVAL; | |
136 | goto out; | |
137 | } | |
138 | ||
139 | ret = ww_mutex_lock(&mutex, &ctx); | |
140 | if (ret != -EALREADY) { | |
141 | pr_err("%s: missed deadlock for recursing, ret=%d\n", | |
142 | __func__, ret); | |
143 | if (!ret) | |
144 | ww_mutex_unlock(&mutex); | |
145 | ret = -EINVAL; | |
146 | goto out; | |
147 | } | |
148 | ||
149 | ret = 0; | |
150 | out: | |
151 | ww_mutex_unlock(&mutex); | |
152 | ww_acquire_fini(&ctx); | |
153 | return ret; | |
154 | } | |
155 | ||
70207686 CW |
156 | struct test_abba { |
157 | struct work_struct work; | |
158 | struct ww_mutex a_mutex; | |
159 | struct ww_mutex b_mutex; | |
160 | struct completion a_ready; | |
161 | struct completion b_ready; | |
162 | bool resolve; | |
163 | int result; | |
164 | }; | |
165 | ||
166 | static void test_abba_work(struct work_struct *work) | |
167 | { | |
168 | struct test_abba *abba = container_of(work, typeof(*abba), work); | |
169 | struct ww_acquire_ctx ctx; | |
170 | int err; | |
171 | ||
172 | ww_acquire_init(&ctx, &ww_class); | |
173 | ww_mutex_lock(&abba->b_mutex, &ctx); | |
174 | ||
175 | complete(&abba->b_ready); | |
176 | wait_for_completion(&abba->a_ready); | |
177 | ||
178 | err = ww_mutex_lock(&abba->a_mutex, &ctx); | |
179 | if (abba->resolve && err == -EDEADLK) { | |
180 | ww_mutex_unlock(&abba->b_mutex); | |
181 | ww_mutex_lock_slow(&abba->a_mutex, &ctx); | |
182 | err = ww_mutex_lock(&abba->b_mutex, &ctx); | |
183 | } | |
184 | ||
185 | if (!err) | |
186 | ww_mutex_unlock(&abba->a_mutex); | |
187 | ww_mutex_unlock(&abba->b_mutex); | |
188 | ww_acquire_fini(&ctx); | |
189 | ||
190 | abba->result = err; | |
191 | } | |
192 | ||
193 | static int test_abba(bool resolve) | |
194 | { | |
195 | struct test_abba abba; | |
196 | struct ww_acquire_ctx ctx; | |
197 | int err, ret; | |
198 | ||
199 | ww_mutex_init(&abba.a_mutex, &ww_class); | |
200 | ww_mutex_init(&abba.b_mutex, &ww_class); | |
201 | INIT_WORK_ONSTACK(&abba.work, test_abba_work); | |
202 | init_completion(&abba.a_ready); | |
203 | init_completion(&abba.b_ready); | |
204 | abba.resolve = resolve; | |
205 | ||
206 | schedule_work(&abba.work); | |
207 | ||
208 | ww_acquire_init(&ctx, &ww_class); | |
209 | ww_mutex_lock(&abba.a_mutex, &ctx); | |
210 | ||
211 | complete(&abba.a_ready); | |
212 | wait_for_completion(&abba.b_ready); | |
213 | ||
214 | err = ww_mutex_lock(&abba.b_mutex, &ctx); | |
215 | if (resolve && err == -EDEADLK) { | |
216 | ww_mutex_unlock(&abba.a_mutex); | |
217 | ww_mutex_lock_slow(&abba.b_mutex, &ctx); | |
218 | err = ww_mutex_lock(&abba.a_mutex, &ctx); | |
219 | } | |
220 | ||
221 | if (!err) | |
222 | ww_mutex_unlock(&abba.b_mutex); | |
223 | ww_mutex_unlock(&abba.a_mutex); | |
224 | ww_acquire_fini(&ctx); | |
225 | ||
226 | flush_work(&abba.work); | |
227 | destroy_work_on_stack(&abba.work); | |
228 | ||
229 | ret = 0; | |
230 | if (resolve) { | |
231 | if (err || abba.result) { | |
232 | pr_err("%s: failed to resolve ABBA deadlock, A err=%d, B err=%d\n", | |
233 | __func__, err, abba.result); | |
234 | ret = -EINVAL; | |
235 | } | |
236 | } else { | |
237 | if (err != -EDEADLK && abba.result != -EDEADLK) { | |
238 | pr_err("%s: missed ABBA deadlock, A err=%d, B err=%d\n", | |
239 | __func__, err, abba.result); | |
240 | ret = -EINVAL; | |
241 | } | |
242 | } | |
243 | return ret; | |
244 | } | |
245 | ||
f2a5fec1 CW |
246 | static int __init test_ww_mutex_init(void) |
247 | { | |
248 | int ret; | |
249 | ||
250 | ret = test_mutex(); | |
251 | if (ret) | |
252 | return ret; | |
253 | ||
c22fb380 CW |
254 | ret = test_aa(); |
255 | if (ret) | |
256 | return ret; | |
257 | ||
70207686 CW |
258 | ret = test_abba(false); |
259 | if (ret) | |
260 | return ret; | |
261 | ||
262 | ret = test_abba(true); | |
263 | if (ret) | |
264 | return ret; | |
265 | ||
f2a5fec1 CW |
266 | return 0; |
267 | } | |
268 | ||
269 | static void __exit test_ww_mutex_exit(void) | |
270 | { | |
271 | } | |
272 | ||
273 | module_init(test_ww_mutex_init); | |
274 | module_exit(test_ww_mutex_exit); | |
275 | ||
276 | MODULE_LICENSE("GPL"); | |
277 | MODULE_AUTHOR("Intel Corporation"); |