1#include "sync/tests/test_internal.h"
2
3#ifdef DEBUG_LOCK_CHK
4
5#include <sync/lock_chk_internal.h>
6#include <sync/mutex.h>
7#include <sync/mutex_simple.h>
8#include <sync/qspinlock.h>
9#include <sync/rwlock.h>
10#include <sync/spinlock.h>
11
12LOCK_CHK_CLASS_DECLARE_LOCAL(graph_test_class_a);
13LOCK_CHK_CLASS_DECLARE_LOCAL(graph_test_class_b);
14LOCK_CHK_CLASS_DECLARE_LOCAL(graph_test_class_c);
15
16TEST_DECLARE_UNIT(lock_chk, graph_node_resolution) {
17 static struct lock_chk_graph graph;
18 lock_chk_graph_init(&graph);
19
20 struct lock_chk_map map_a =
21 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_a));
22 struct lock_chk_map map_b =
23 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_b));
24
25 struct lock_chk_node *node_a0 = NULL;
26 struct lock_chk_node *node_a0_again = NULL;
27 struct lock_chk_node *node_a1 = NULL;
28 struct lock_chk_node *node_b0 = NULL;
29
30 TEST_ASSERT_EQ(
31 lock_chk_graph_resolve_node(&graph, &map_a, 0, NULL, &node_a0),
32 LOCK_CHK_RESULT_OK);
33 TEST_ASSERT_NONNULL(node_a0);
34
35 TEST_ASSERT_EQ(
36 lock_chk_graph_resolve_node(&graph, &map_a, 0, NULL, &node_a0_again),
37 LOCK_CHK_RESULT_OK);
38 TEST_ASSERT_PTR_EQ(node_a0, node_a0_again);
39
40 TEST_ASSERT_EQ(
41 lock_chk_graph_resolve_node(&graph, &map_a, 1, NULL, &node_a1),
42 LOCK_CHK_RESULT_OK);
43 TEST_ASSERT_NONNULL(node_a1);
44 TEST_ASSERT_PTR_NE(node_a1, node_a0);
45 TEST_ASSERT_EQ(node_a1->subclass, 1);
46
47 TEST_ASSERT_EQ(
48 lock_chk_graph_resolve_node(&graph, &map_b, 0, NULL, &node_b0),
49 LOCK_CHK_RESULT_OK);
50 TEST_ASSERT_NONNULL(node_b0);
51 TEST_ASSERT_PTR_NE(node_b0, node_a0);
52
53 return TEST_SUCCESS;
54}
55
56TEST_DECLARE_UNIT(lock_chk, graph_cycle_detection) {
57 static struct lock_chk_graph graph;
58 lock_chk_graph_init(&graph);
59
60 struct lock_chk_map map_a =
61 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_a));
62 struct lock_chk_map map_b =
63 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_b));
64 struct lock_chk_map map_c =
65 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_c));
66
67 struct lock_chk_node *node_a = NULL;
68 struct lock_chk_node *node_b = NULL;
69 struct lock_chk_node *node_c = NULL;
70
71 TEST_ASSERT_EQ(
72 lock_chk_graph_resolve_node(&graph, &map_a, 0, NULL, &node_a),
73 LOCK_CHK_RESULT_OK);
74 TEST_ASSERT_EQ(
75 lock_chk_graph_resolve_node(&graph, &map_b, 0, NULL, &node_b),
76 LOCK_CHK_RESULT_OK);
77 TEST_ASSERT_EQ(
78 lock_chk_graph_resolve_node(&graph, &map_c, 0, NULL, &node_c),
79 LOCK_CHK_RESULT_OK);
80
81 /* A -> B */
82 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
83 &graph, node_a, LOCK_CHK_MODE_EXCLUSIVE, node_b,
84 LOCK_CHK_MODE_EXCLUSIVE, NULL, NULL),
85 LOCK_CHK_RESULT_OK);
86 TEST_ASSERT_EQ(graph.edge_count, 1);
87
88 /* Duplicate A -> B should be dedup without adding edge */
89 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
90 &graph, node_a, LOCK_CHK_MODE_EXCLUSIVE, node_b,
91 LOCK_CHK_MODE_EXCLUSIVE, NULL, NULL),
92 LOCK_CHK_RESULT_OK);
93 TEST_ASSERT_EQ(graph.edge_count, 1);
94
95 /* B -> C */
96 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
97 &graph, node_b, LOCK_CHK_MODE_EXCLUSIVE, node_c,
98 LOCK_CHK_MODE_EXCLUSIVE, NULL, NULL),
99 LOCK_CHK_RESULT_OK);
100 TEST_ASSERT_EQ(graph.edge_count, 2);
101
102 /* C -> A completes cycle A -> B -> C -> A reports CYCLE */
103 struct lock_chk_failure fail = {0};
104 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
105 &graph, node_c, LOCK_CHK_MODE_EXCLUSIVE, node_a,
106 LOCK_CHK_MODE_EXCLUSIVE, NULL, &fail),
107 LOCK_CHK_RESULT_CYCLE);
108 TEST_ASSERT_EQ(fail.cycle_len, 3);
109 TEST_ASSERT_NE(fail.signature, 0);
110
111 /* Direct B -> A completes 2-node cycle: must report CYCLE */
112 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
113 &graph, node_b, LOCK_CHK_MODE_EXCLUSIVE, node_a,
114 LOCK_CHK_MODE_EXCLUSIVE, NULL, NULL),
115 LOCK_CHK_RESULT_CYCLE);
116
117 return TEST_SUCCESS;
118}
119
120TEST_DECLARE_UNIT(lock_chk, irq_safety_conflict) {
121 static struct lock_chk_graph graph;
122 lock_chk_graph_init(&graph);
123
124 struct lock_chk_map map_disp =
125 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_a));
126 struct lock_chk_map map_high =
127 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_b));
128
129 struct lock_chk_acquire_request req_disp = {
130 .type = LOCK_CHK_TYPE_SPIN,
131 .prev_irql = IRQL_PASSIVE_LEVEL,
132 .irqs_enabled = true,
133 .irq_safe = false,
134 .raw_operation = false,
135 .in_irq = false,
136 .in_nmi = false,
137 };
138 struct lock_chk_acquire_request req_high = {
139 .type = LOCK_CHK_TYPE_SPIN,
140 .prev_irql = IRQL_HIGH_LEVEL,
141 .irqs_enabled = false,
142 .irq_safe = true,
143 .raw_operation = false,
144 .in_irq = false,
145 .in_nmi = false,
146 };
147
148 struct lock_chk_node *node = NULL;
149
150 TEST_ASSERT_EQ(
151 lock_chk_graph_resolve_node(&graph, &map_disp, 0, &req_disp, &node),
152 LOCK_CHK_RESULT_OK);
153 TEST_ASSERT_EQ(
154 lock_chk_graph_resolve_node(&graph, &map_disp, 0, &req_high, &node),
155 LOCK_CHK_RESULT_BAD_CONTEXT);
156
157 node = NULL;
158 TEST_ASSERT_EQ(
159 lock_chk_graph_resolve_node(&graph, &map_high, 0, &req_high, &node),
160 LOCK_CHK_RESULT_OK);
161 TEST_ASSERT_EQ(
162 lock_chk_graph_resolve_node(&graph, &map_high, 0, &req_disp, &node),
163 LOCK_CHK_RESULT_BAD_CONTEXT);
164
165 return TEST_SUCCESS;
166}
167
168TEST_DECLARE_UNIT(lock_chk, graph_acquire_dedup) {
169 static struct lock_chk_graph graph;
170 lock_chk_graph_init(&graph);
171
172 struct lock_chk_map map_a =
173 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_a));
174 struct lock_chk_map map_b =
175 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_b));
176 struct lock_chk_node *node_a = NULL;
177 struct lock_chk_node *node_b = NULL;
178
179 TEST_ASSERT_EQ(
180 lock_chk_graph_resolve_node(&graph, &map_a, 0, NULL, &node_a),
181 LOCK_CHK_RESULT_OK);
182
183 struct lock_chk_thread_data held = {
184 .held =
185 {
186 {.node = node_a,
187 .flags = LOCK_CHKD_ORDER,
188 .mode = LOCK_CHK_MODE_EXCLUSIVE},
189 {.node = node_a,
190 .flags = LOCK_CHKD_ORDER,
191 .mode = LOCK_CHK_MODE_EXCLUSIVE},
192 },
193 .depth = 2,
194 };
195 struct lock_chk_acquire_request request = {
196 .map = &map_b,
197 .flags = LOCK_CHKD_ORDER,
198 .type = LOCK_CHK_TYPE_MUTEX,
199 .mode = LOCK_CHK_MODE_EXCLUSIVE,
200 .wait_kind = LOCK_CHK_WAIT_BLOCKING,
201 };
202 struct lock_chk_failure failure = {0};
203
204 TEST_ASSERT_EQ(lock_chk_graph_prepare_acquire(&graph, &map_b, 0, &request,
205 &held, &node_b, &failure),
206 LOCK_CHK_RESULT_OK);
207 TEST_ASSERT_NONNULL(node_b);
208 TEST_ASSERT_EQ(graph.edge_count, 1);
209 return TEST_SUCCESS;
210}
211
212TEST_DECLARE_UNIT(lock_chk, graph_acquire_rollback) {
213 static struct lock_chk_graph graph;
214 lock_chk_graph_init(&graph);
215
216 struct lock_chk_map map_a =
217 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_a));
218 struct lock_chk_map map_b =
219 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_b));
220 struct lock_chk_node *node_a = NULL;
221 struct lock_chk_node *node_b = NULL;
222
223 TEST_ASSERT_EQ(
224 lock_chk_graph_resolve_node(&graph, &map_a, 0, NULL, &node_a),
225 LOCK_CHK_RESULT_OK);
226
227 struct lock_chk_thread_data held = {
228 .held = {{.node = node_a,
229 .flags = LOCK_CHKD_ORDER,
230 .mode = LOCK_CHK_MODE_EXCLUSIVE}},
231 .depth = 1,
232 };
233 struct lock_chk_acquire_request request = {
234 .map = &map_b,
235 .flags = LOCK_CHKD_ORDER,
236 .type = LOCK_CHK_TYPE_MUTEX,
237 .mode = LOCK_CHK_MODE_EXCLUSIVE,
238 .wait_kind = LOCK_CHK_WAIT_BLOCKING,
239 };
240 struct lock_chk_failure failure = {0};
241 uint16_t nodes_before = graph.node_count;
242 graph.edge_count = LOCK_CHK_MAX_EDGES;
243
244 TEST_ASSERT_EQ(lock_chk_graph_prepare_acquire(&graph, &map_b, 0, &request,
245 &held, &node_b, &failure),
246 LOCK_CHK_RESULT_EDGE_CAPACITY);
247 TEST_ASSERT_NULL(node_b);
248 TEST_ASSERT_EQ(graph.node_count, nodes_before);
249 TEST_ASSERT_NULL(
250 atomic_load_explicit(&map_b.base_node, memory_order_relaxed));
251 TEST_ASSERT_EQ(failure.kind, LOCK_CHK_FAIL_CAPACITY);
252 return TEST_SUCCESS;
253}
254
255TEST_DECLARE_UNIT(lock_chk, spin_qspin_lifecycle) {
256 struct spinlock spin_disp;
257 struct spinlock spin_irq;
258 struct spinlock spin_raw;
259 struct qspinlock qspin_disp;
260 struct qspinlock qspin_irq;
261 struct qspinlock qspin_raw;
262 enum irql irql;
263
264 spinlock_init(&spin_disp);
265 spinlock_init(&spin_irq);
266 spinlock_init(&spin_raw);
267 qspinlock_init(&qspin_disp);
268 qspinlock_init(&qspin_irq);
269 qspinlock_init(&qspin_raw);
270
271 irql = spin_lock(&spin_disp);
272 spin_unlock(&spin_disp, irql);
273
274 TEST_ASSERT(spin_trylock(&spin_disp, &irql));
275 spin_unlock(&spin_disp, irql);
276
277 irql = spin_lock_irq_disable(&spin_irq);
278 spin_unlock(&spin_irq, irql);
279
280 TEST_ASSERT(spin_trylock_irq_disable(&spin_irq, &irql));
281 spin_unlock(&spin_irq, irql);
282
283 spin_lock_raw(&spin_raw);
284 spin_unlock_raw(&spin_raw);
285
286 TEST_ASSERT(spin_trylock_raw(&spin_raw));
287 spin_unlock_raw(&spin_raw);
288
289 irql = qspin_lock(&qspin_disp);
290 qspin_unlock(&qspin_disp, irql);
291
292 TEST_ASSERT(qspin_trylock(&qspin_disp, &irql));
293 qspin_unlock(&qspin_disp, irql);
294
295 irql = qspin_lock_irq_disable(&qspin_irq);
296 qspin_unlock(&qspin_irq, irql);
297
298 TEST_ASSERT(qspin_trylock_irq_disable(&qspin_irq, &irql));
299 qspin_unlock(&qspin_irq, irql);
300
301 qspin_lock_raw(&qspin_raw);
302 qspin_unlock_raw(&qspin_raw);
303
304 TEST_ASSERT(qspin_trylock_raw(&qspin_raw));
305 qspin_unlock_raw(&qspin_raw);
306
307 return TEST_SUCCESS;
308}
309
310TEST_DECLARE_UNIT(lock_chk, mutex_out_of_order_release) {
311 struct mutex m1;
312 struct mutex m2;
313
314 mutex_init(&m1);
315 mutex_init(&m2);
316
317 mutex_lock(&m1);
318 mutex_lock(&m2);
319
320 mutex_unlock(&m1);
321 mutex_unlock(&m2);
322
323 return TEST_SUCCESS;
324}
325
326TEST_DECLARE_UNIT(lock_chk, mutex_simple_lifecycle) {
327 struct mutex_simple s1;
328 struct mutex_simple s2;
329
330 mutex_simple_init(&s1);
331 mutex_simple_init(&s2);
332
333 mutex_simple_lock(&s1);
334 mutex_simple_unlock(&s1);
335
336 mutex_simple_lock_subclass(&s1, 1);
337 mutex_simple_unlock(&s1);
338
339 mutex_simple_lock(&s1);
340 mutex_simple_lock(&s2);
341
342 mutex_simple_unlock(&s1);
343 mutex_simple_unlock(&s2);
344
345 return TEST_SUCCESS;
346}
347
348TEST_DECLARE_UNIT(lock_chk, rw_reader_ring_and_conflict) {
349 static struct lock_chk_graph graph;
350 lock_chk_graph_init(&graph);
351
352 struct lock_chk_map map_a =
353 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_a));
354 struct lock_chk_map map_b =
355 LOCK_CHK_MAP_VALUE_INIT(LOCK_CHK_CLASS(graph_test_class_b));
356
357 struct lock_chk_node *node_a = NULL;
358 struct lock_chk_node *node_b = NULL;
359
360 TEST_ASSERT_EQ(
361 lock_chk_graph_resolve_node(&graph, &map_a, 0, NULL, &node_a),
362 LOCK_CHK_RESULT_OK);
363 TEST_ASSERT_EQ(
364 lock_chk_graph_resolve_node(&graph, &map_b, 0, NULL, &node_b),
365 LOCK_CHK_RESULT_OK);
366
367 TEST_ASSERT_EQ(
368 lock_chk_graph_add_dependency(&graph, node_a, LOCK_CHK_MODE_SHARED,
369 node_b, LOCK_CHK_MODE_SHARED, NULL, NULL),
370 LOCK_CHK_RESULT_OK);
371 TEST_ASSERT_EQ(
372 lock_chk_graph_add_dependency(&graph, node_b, LOCK_CHK_MODE_SHARED,
373 node_a, LOCK_CHK_MODE_SHARED, NULL, NULL),
374 LOCK_CHK_RESULT_OK);
375
376 lock_chk_graph_init(&graph);
377 TEST_ASSERT_EQ(
378 lock_chk_graph_resolve_node(&graph, &map_a, 0, NULL, &node_a),
379 LOCK_CHK_RESULT_OK);
380 TEST_ASSERT_EQ(
381 lock_chk_graph_resolve_node(&graph, &map_b, 0, NULL, &node_b),
382 LOCK_CHK_RESULT_OK);
383
384 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
385 &graph, node_a, LOCK_CHK_MODE_SHARED, node_b,
386 LOCK_CHK_MODE_EXCLUSIVE, NULL, NULL),
387 LOCK_CHK_RESULT_OK);
388 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
389 &graph, node_b, LOCK_CHK_MODE_SHARED, node_a,
390 LOCK_CHK_MODE_EXCLUSIVE, NULL, NULL),
391 LOCK_CHK_RESULT_CYCLE);
392
393 lock_chk_graph_init(&graph);
394 TEST_ASSERT_EQ(
395 lock_chk_graph_resolve_node(&graph, &map_a, 0, NULL, &node_a),
396 LOCK_CHK_RESULT_OK);
397 TEST_ASSERT_EQ(
398 lock_chk_graph_resolve_node(&graph, &map_b, 0, NULL, &node_b),
399 LOCK_CHK_RESULT_OK);
400
401 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
402 &graph, node_a, LOCK_CHK_MODE_EXCLUSIVE, node_b,
403 LOCK_CHK_MODE_EXCLUSIVE, NULL, NULL),
404 LOCK_CHK_RESULT_OK);
405 TEST_ASSERT_EQ(lock_chk_graph_add_dependency(
406 &graph, node_b, LOCK_CHK_MODE_EXCLUSIVE, node_a,
407 LOCK_CHK_MODE_EXCLUSIVE, NULL, NULL),
408 LOCK_CHK_RESULT_CYCLE);
409
410 return TEST_SUCCESS;
411}
412
413TEST_DECLARE_UNIT(lock_chk, rwlock_lifecycle) {
414 struct rwlock rw;
415 rwlock_init(&rw, THREAD_PRIO_CLASS_TIMESHARE);
416
417 /* Read acquire and release */
418 rw_read_lock(&rw);
419 rw_unlock(&rw);
420
421 /* Write acquire and release */
422 rw_write_lock(&rw);
423 rw_unlock(&rw);
424
425 /* Subclass read and write */
426 rw_read_lock_subclass(&rw, 1);
427 rw_unlock(&rw);
428
429 rw_write_lock_subclass(&rw, 2);
430 rw_unlock(&rw);
431
432 return TEST_SUCCESS;
433}
434
435TEST_DECLARE_UNIT(lock_chk, subclasses_all_primitives) {
436 struct spinlock spin;
437 struct qspinlock qspin;
438 struct mutex mtx;
439 struct mutex_simple mtx_s;
440 struct rwlock rw;
441
442 spinlock_init(&spin);
443 qspinlock_init(&qspin);
444 mutex_init(&mtx);
445 mutex_simple_init(&mtx_s);
446 rwlock_init(&rw, THREAD_PRIO_CLASS_TIMESHARE);
447
448 for (unsigned int sc = 0; sc < 8; sc++) {
449 enum irql irql;
450
451 irql = spin_lock_subclass(&spin, sc);
452 spin_unlock(&spin, irql);
453
454 irql = qspin_lock_subclass(&qspin, sc);
455 qspin_unlock(&qspin, irql);
456
457 mutex_lock_subclass(&mtx, sc);
458 mutex_unlock(&mtx);
459
460 mutex_simple_lock_subclass(&mtx_s, sc);
461 mutex_simple_unlock(&mtx_s);
462
463 rw_read_lock_subclass(&rw, sc);
464 rw_unlock(&rw);
465
466 rw_write_lock_subclass(&rw, sc);
467 rw_unlock(&rw);
468 }
469
470 return TEST_SUCCESS;
471}
472
473#endif /* DEBUG_LOCK_CHK */
474