1/* @title: Thread Safety Analysis */
2#pragma once
3#include <compiler/core.h>
4#include <compiler/name.h>
5
6/* Macros around clang's -Wthread-safety features. Does nothing on GCC
7 *
8 * Use like this:
9 * struct list_head list TSA_GUARDED_BY(lock);
10 * struct spinlock lock;
11 *
12 * NOTE: referencing a lock declared later in the struct is given by
13 * -fexperimental-late-parse-attributes
14 */
15
16#if defined(cn_id_clang)
17
18#define TSA_CAPABILITY(kind) __attribute__((capability(kind)))
19
20/* for regular data */
21#define TSA_GUARDED_BY(...) __attribute__((guarded_by(__VA_ARGS__)))
22
23/* This guards the data a POINTER points at, pointer itself is not guarded */
24#define TSA_PTR_GUARDED_BY(...) __attribute__((pt_guarded_by(__VA_ARGS__)))
25
26/* Function verification */
27#define TSA_CALLER_HOLDS(...) __attribute__((requires_capability(__VA_ARGS__)))
28#define TSA_CALLER_HOLDS_SHARED(...) \
29 __attribute__((requires_shared_capability(__VA_ARGS__)))
30#define TSA_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
31
32/* Lock primitives and wrappers */
33#define TSA_ACQUIRES(...) __attribute__((acquire_capability(__VA_ARGS__)))
34#define TSA_ACQUIRES_SHARED(...) \
35 __attribute__((acquire_shared_capability(__VA_ARGS__)))
36#define TSA_RELEASES(...) __attribute__((release_capability(__VA_ARGS__)))
37#define TSA_RELEASES_SHARED(...) \
38 __attribute__((release_shared_capability(__VA_ARGS__)))
39
40/* Releases whatever mode is held (rwlock unlock) */
41#define TSA_RELEASES_GENERIC(...) \
42 __attribute__((release_generic_capability(__VA_ARGS__)))
43#define TSA_TRY_ACQUIRES(ret, ...) \
44 __attribute__((try_acquire_capability(ret, __VA_ARGS__)))
45#define TSA_TRY_ACQUIRES_SHARED(ret, ...) \
46 __attribute__((try_acquire_shared_capability(ret, __VA_ARGS__)))
47#define TSA_ASSERT_CAPABILITY(...) \
48 __attribute__((assert_capability(__VA_ARGS__)))
49#define TSA_ASSERT_SHARED_CAPABILITY(...) \
50 __attribute__((assert_shared_capability(__VA_ARGS__)))
51
52/* function returns (a pointer to) this capability, for guards */
53#define TSA_RETURNS_CAPABILITY(cap) __attribute__((lock_returned(cap)))
54
55/* static ordering between sibling members or globals */
56#define TSA_ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
57#define TSA_ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
58
59/* escape hatch */
60#define TSA_NO_ANALYSIS __attribute__((no_thread_safety_analysis))
61
62/* For intentionally racy accesses and ganders */
63#define TSA_UNSAFE(expr) \
64 ({ \
65 _Pragma("clang diagnostic push"); \
66 _Pragma("clang diagnostic ignored \"-Wthread-safety-analysis\""); \
67 _Pragma("clang diagnostic ignored \"-Wthread-safety-pointer\""); \
68 __auto_type __tsa_unsafe_v = (expr); \
69 _Pragma("clang diagnostic pop"); \
70 __tsa_unsafe_v; \
71 })
72
73/* tsa_unpublished(&o->lock) states that o is not reachable by anyone else yet,
74 * so we simply treat the lock as held for the rest of the function */
75#define TSA_DEFINE_ASSUME(type) \
76 static inline cc_overloadable cc_always_inline cc_unused void \
77 tsa_unpublished_internal(type *cap) TSA_ASSERT_CAPABILITY(cap) { \
78 cc_ignore(cap); \
79 }
80#define tsa_unpublished(cap) tsa_unpublished_internal(cap)
81
82#define TSA_CAPABILITY_DECLARE(name, symbol) \
83 struct TSA_CAPABILITY(name) __##symbol##_ctx {}; \
84 extern struct __##symbol##_ctx symbol
85
86/* IRQL tracking. Reentrant capabilities give us nested raises and spinlocks,
87 * and we exclude IRQL_RAISED for sleeping primitives like mutexes */
88#if cn_has_attribute(reentrant_capability)
89#define TSA_IRQL_TRACKS_SPINLOCKS 1
90#define TSA_REENTRANT_CAPABILITY_DECLARE(name, symbol) \
91 struct TSA_CAPABILITY(name) \
92 __attribute__((reentrant_capability)) __##symbol##_ctx {}; \
93 extern struct __##symbol##_ctx symbol
94#else
95#define TSA_IRQL_TRACKS_SPINLOCKS 0
96#define TSA_REENTRANT_CAPABILITY_DECLARE(name, symbol) \
97 TSA_CAPABILITY_DECLARE(name, symbol)
98#endif
99
100#if TSA_IRQL_TRACKS_SPINLOCKS
101#define TSA_ACQUIRES_SPIN(lock) \
102 __attribute__((acquire_capability(lock, IRQL_RAISED)))
103#define TSA_RELEASES_SPIN(lock) \
104 __attribute__((release_capability(lock, IRQL_RAISED)))
105#define TSA_TRY_ACQUIRES_SPIN(ret, lock) \
106 __attribute__((try_acquire_capability(ret, lock, IRQL_RAISED)))
107#define TSA_ACQUIRES_SPIN_IRQL TSA_ACQUIRES(IRQL_RAISED)
108#define TSA_RELEASES_SPIN_IRQL TSA_RELEASES(IRQL_RAISED)
109#else
110#define TSA_ACQUIRES_SPIN(lock) TSA_ACQUIRES(lock)
111#define TSA_RELEASES_SPIN(lock) TSA_RELEASES(lock)
112#define TSA_TRY_ACQUIRES_SPIN(ret, lock) TSA_TRY_ACQUIRES(ret, lock)
113#define TSA_ACQUIRES_SPIN_IRQL
114#define TSA_RELEASES_SPIN_IRQL
115#endif
116
117/* Interrupt primitives, just like IRQL raises, no
118 * irq sleeping with MAY_SLEEP */
119#if TSA_IRQL_TRACKS_SPINLOCKS
120#define TSA_ACQUIRES_IRQS TSA_ACQUIRES(IRQS_DISABLED)
121#define TSA_RELEASES_IRQS TSA_RELEASES(IRQS_DISABLED)
122#define TSA_ACQUIRES_IRQS_LOCK(lock) \
123 __attribute__((acquire_capability(lock, IRQS_DISABLED)))
124#define TSA_RELEASES_IRQS_LOCK(lock) \
125 __attribute__((release_capability(lock, IRQS_DISABLED)))
126#define TSA_MAY_SLEEP \
127 __attribute__((locks_excluded(IRQL_RAISED, IRQS_DISABLED)))
128#else
129#define TSA_ACQUIRES_IRQS
130#define TSA_RELEASES_IRQS
131#define TSA_ACQUIRES_IRQS_LOCK(lock) TSA_ACQUIRES(lock)
132#define TSA_RELEASES_IRQS_LOCK(lock) TSA_RELEASES(lock)
133#define TSA_MAY_SLEEP TSA_EXCLUDED(IRQL_RAISED)
134#endif
135
136#else
137
138#define TSA_CAPABILITY(kind)
139#define TSA_GUARDED_BY(...)
140#define TSA_PT_GUARDED_BY(...)
141#define TSA_CALLER_HOLDS(...)
142#define TSA_CALLER_HOLDS_SHARED(...)
143#define TSA_EXCLUDED(...)
144#define TSA_ACQUIRES(...)
145#define TSA_ACQUIRES_SHARED(...)
146#define TSA_RELEASES(...)
147#define TSA_RELEASES_SHARED(...)
148#define TSA_RELEASES_GENERIC(...)
149#define TSA_TRY_ACQUIRES(ret, ...)
150#define TSA_TRY_ACQUIRES_SHARED(ret, ...)
151#define TSA_ASSERT_CAPABILITY(...)
152#define TSA_ASSERT_SHARED_CAPABILITY(...)
153#define TSA_RETURNS_CAPABILITY(cap)
154#define TSA_ACQUIRED_AFTER(...)
155#define TSA_ACQUIRED_BEFORE(...)
156#define TSA_NO_ANALYSIS
157#define TSA_UNSAFE(expr) (expr)
158#define TSA_DEFINE_ASSUME(type)
159#define tsa_unpublished(cap) ((void) (cap))
160#define TSA_CAPABILITY_DECLARE(name, symbol)
161#define TSA_IRQL_TRACKS_SPINLOCKS 0
162#define TSA_REENTRANT_CAPABILITY_DECLARE(name, symbol)
163#define TSA_ACQUIRES_SPIN(lock)
164#define TSA_RELEASES_SPIN(lock)
165#define TSA_TRY_ACQUIRES_SPIN(ret, lock)
166#define TSA_ACQUIRES_SPIN_IRQL
167#define TSA_RELEASES_SPIN_IRQL
168#define TSA_ACQUIRES_IRQS
169#define TSA_RELEASES_IRQS
170#define TSA_ACQUIRES_IRQS_LOCK(lock)
171#define TSA_RELEASES_IRQS_LOCK(lock)
172#define TSA_MAY_SLEEP
173
174#endif
175
176/* Token for fields that only a certain thread (the owner) should touch,
177 * like
178 *
179 * TSA_OWNER_TOKEN(self);
180 * uint32_t rcu_nesting TSA_GUARDED_BY(self);
181 *
182 * Code running under the owner says tsa_owner_is_me(&t->self) */
183struct TSA_CAPABILITY("owner") tsa_owner {};
184#define TSA_OWNER_TOKEN(name) struct tsa_owner name
185
186static inline cc_always_inline
187cc_unused void tsa_owner_is_me(struct tsa_owner *tok)
188 TSA_ASSERT_CAPABILITY(tok) {
189 cc_ignore(tok);
190}
191