xref: /linux/lib/test_context-analysis.c (revision f16a802d402d735a55731f8c94952b3bbb5ddfe8)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Compile-only tests for common patterns that should not generate false
4  * positive errors when compiled with Clang's context analysis.
5  */
6 
7 #include <linux/build_bug.h>
8 #include <linux/spinlock.h>
9 
10 /*
11  * Test that helper macros work as expected.
12  */
13 static void __used test_common_helpers(void)
14 {
15 	BUILD_BUG_ON(context_unsafe(3) != 3); /* plain expression */
16 	BUILD_BUG_ON(context_unsafe((void)2; 3) != 3); /* does not swallow semi-colon */
17 	BUILD_BUG_ON(context_unsafe((void)2, 3) != 3); /* does not swallow commas */
18 	context_unsafe(do { } while (0)); /* works with void statements */
19 }
20 
21 #define TEST_SPINLOCK_COMMON(class, type, type_init, type_lock, type_unlock, type_trylock, op)	\
22 	struct test_##class##_data {								\
23 		type lock;									\
24 		int counter __guarded_by(&lock);						\
25 		int *pointer __pt_guarded_by(&lock);						\
26 	};											\
27 	static void __used test_##class##_init(struct test_##class##_data *d)			\
28 	{											\
29 		type_init(&d->lock);								\
30 		d->counter = 0;									\
31 	}											\
32 	static void __used test_##class(struct test_##class##_data *d)				\
33 	{											\
34 		unsigned long flags;								\
35 		d->pointer++;									\
36 		type_lock(&d->lock);								\
37 		op(d->counter);									\
38 		op(*d->pointer);								\
39 		type_unlock(&d->lock);								\
40 		type_lock##_irq(&d->lock);							\
41 		op(d->counter);									\
42 		op(*d->pointer);								\
43 		type_unlock##_irq(&d->lock);							\
44 		type_lock##_bh(&d->lock);							\
45 		op(d->counter);									\
46 		op(*d->pointer);								\
47 		type_unlock##_bh(&d->lock);							\
48 		type_lock##_irqsave(&d->lock, flags);						\
49 		op(d->counter);									\
50 		op(*d->pointer);								\
51 		type_unlock##_irqrestore(&d->lock, flags);					\
52 	}											\
53 	static void __used test_##class##_trylock(struct test_##class##_data *d)		\
54 	{											\
55 		if (type_trylock(&d->lock)) {							\
56 			op(d->counter);								\
57 			type_unlock(&d->lock);							\
58 		}										\
59 	}											\
60 	static void __used test_##class##_assert(struct test_##class##_data *d)			\
61 	{											\
62 		lockdep_assert_held(&d->lock);							\
63 		op(d->counter);									\
64 	}											\
65 	static void __used test_##class##_guard(struct test_##class##_data *d)			\
66 	{											\
67 		{ guard(class)(&d->lock);		op(d->counter); }			\
68 		{ guard(class##_irq)(&d->lock);		op(d->counter); }			\
69 		{ guard(class##_irqsave)(&d->lock);	op(d->counter); }			\
70 	}
71 
72 #define TEST_OP_RW(x) (x)++
73 #define TEST_OP_RO(x) ((void)(x))
74 
75 TEST_SPINLOCK_COMMON(raw_spinlock,
76 		     raw_spinlock_t,
77 		     raw_spin_lock_init,
78 		     raw_spin_lock,
79 		     raw_spin_unlock,
80 		     raw_spin_trylock,
81 		     TEST_OP_RW);
82 static void __used test_raw_spinlock_trylock_extra(struct test_raw_spinlock_data *d)
83 {
84 	unsigned long flags;
85 
86 	if (raw_spin_trylock_irq(&d->lock)) {
87 		d->counter++;
88 		raw_spin_unlock_irq(&d->lock);
89 	}
90 	if (raw_spin_trylock_irqsave(&d->lock, flags)) {
91 		d->counter++;
92 		raw_spin_unlock_irqrestore(&d->lock, flags);
93 	}
94 	scoped_cond_guard(raw_spinlock_try, return, &d->lock) {
95 		d->counter++;
96 	}
97 }
98 
99 TEST_SPINLOCK_COMMON(spinlock,
100 		     spinlock_t,
101 		     spin_lock_init,
102 		     spin_lock,
103 		     spin_unlock,
104 		     spin_trylock,
105 		     TEST_OP_RW);
106 static void __used test_spinlock_trylock_extra(struct test_spinlock_data *d)
107 {
108 	unsigned long flags;
109 
110 	if (spin_trylock_irq(&d->lock)) {
111 		d->counter++;
112 		spin_unlock_irq(&d->lock);
113 	}
114 	if (spin_trylock_irqsave(&d->lock, flags)) {
115 		d->counter++;
116 		spin_unlock_irqrestore(&d->lock, flags);
117 	}
118 	scoped_cond_guard(spinlock_try, return, &d->lock) {
119 		d->counter++;
120 	}
121 }
122 
123 TEST_SPINLOCK_COMMON(write_lock,
124 		     rwlock_t,
125 		     rwlock_init,
126 		     write_lock,
127 		     write_unlock,
128 		     write_trylock,
129 		     TEST_OP_RW);
130 static void __used test_write_trylock_extra(struct test_write_lock_data *d)
131 {
132 	unsigned long flags;
133 
134 	if (write_trylock_irqsave(&d->lock, flags)) {
135 		d->counter++;
136 		write_unlock_irqrestore(&d->lock, flags);
137 	}
138 }
139 
140 TEST_SPINLOCK_COMMON(read_lock,
141 		     rwlock_t,
142 		     rwlock_init,
143 		     read_lock,
144 		     read_unlock,
145 		     read_trylock,
146 		     TEST_OP_RO);
147