]>
Commit | Line | Data |
---|---|---|
aead9dc9 PB |
1 | /* |
2 | * Graph lock: rwlock to protect block layer graph manipulations (add/remove | |
3 | * edges and nodes) | |
4 | * | |
5 | * Copyright (c) 2022 Red Hat | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | #ifndef GRAPH_LOCK_H | |
21 | #define GRAPH_LOCK_H | |
22 | ||
4002ffdc | 23 | #include "qemu/clang-tsa.h" |
aead9dc9 | 24 | |
aead9dc9 PB |
25 | /** |
26 | * Graph Lock API | |
27 | * This API provides a rwlock used to protect block layer | |
28 | * graph modifications like edge (BdrvChild) and node (BlockDriverState) | |
29 | * addition and removal. | |
30 | * Currently we have 1 writer only, the Main loop, and many | |
31 | * readers, mostly coroutines running in other AioContext thus other threads. | |
32 | * | |
33 | * We distinguish between writer (main loop, under BQL) that modifies the | |
34 | * graph, and readers (all other coroutines running in various AioContext), | |
35 | * that go through the graph edges, reading | |
36 | * BlockDriverState ->parents and->children. | |
37 | * | |
38 | * The writer (main loop) has an "exclusive" access, so it first waits for | |
39 | * current read to finish, and then prevents incoming ones from | |
40 | * entering while it has the exclusive access. | |
41 | * | |
42 | * The readers (coroutines in multiple AioContext) are free to | |
43 | * access the graph as long the writer is not modifying the graph. | |
44 | * In case it is, they go in a CoQueue and sleep until the writer | |
45 | * is done. | |
46 | * | |
47 | * If a coroutine changes AioContext, the counter in the original and new | |
48 | * AioContext are left intact, since the writer does not care where is the | |
49 | * reader, but only if there is one. | |
50 | * As a result, some AioContexts might have a negative reader count, to | |
51 | * balance the positive count of the AioContext that took the lock. | |
52 | * This also means that when an AioContext is deleted it may have a nonzero | |
53 | * reader count. In that case we transfer the count to a global shared counter | |
54 | * so that the writer is always aware of all readers. | |
55 | */ | |
56 | typedef struct BdrvGraphRWlock BdrvGraphRWlock; | |
57 | ||
4002ffdc KW |
58 | /* Dummy lock object to use for Thread Safety Analysis (TSA) */ |
59 | typedef struct TSA_CAPABILITY("mutex") BdrvGraphLock { | |
60 | } BdrvGraphLock; | |
61 | ||
62 | extern BdrvGraphLock graph_lock; | |
63 | ||
64 | /* | |
65 | * clang doesn't check consistency in locking annotations between forward | |
66 | * declarations and the function definition. Having the annotation on the | |
67 | * definition, but not the declaration in a header file, may give the reader | |
68 | * a false sense of security because the condition actually remains unchecked | |
69 | * for callers in other source files. | |
70 | * | |
71 | * Therefore, as a convention, for public functions, GRAPH_RDLOCK and | |
72 | * GRAPH_WRLOCK annotations should be present only in the header file. | |
73 | */ | |
74 | #define GRAPH_WRLOCK TSA_REQUIRES(graph_lock) | |
75 | #define GRAPH_RDLOCK TSA_REQUIRES_SHARED(graph_lock) | |
d51c349b | 76 | #define GRAPH_UNLOCKED TSA_EXCLUDES(graph_lock) |
4002ffdc KW |
77 | |
78 | /* | |
79 | * TSA annotations are not part of function types, so checks are defeated when | |
80 | * using a function pointer. As a workaround, annotate function pointers with | |
81 | * this macro that will require that the lock is at least taken while reading | |
82 | * the pointer. In most cases this is equivalent to actually protecting the | |
83 | * function call. | |
84 | */ | |
85 | #define GRAPH_RDLOCK_PTR TSA_GUARDED_BY(graph_lock) | |
86 | #define GRAPH_WRLOCK_PTR TSA_GUARDED_BY(graph_lock) | |
d51c349b | 87 | #define GRAPH_UNLOCKED_PTR |
4002ffdc | 88 | |
aead9dc9 PB |
89 | /* |
90 | * register_aiocontext: | |
91 | * Add AioContext @ctx to the list of AioContext. | |
92 | * This list is used to obtain the total number of readers | |
93 | * currently running the graph. | |
94 | */ | |
95 | void register_aiocontext(AioContext *ctx); | |
96 | ||
97 | /* | |
98 | * unregister_aiocontext: | |
99 | * Removes AioContext @ctx to the list of AioContext. | |
100 | */ | |
101 | void unregister_aiocontext(AioContext *ctx); | |
102 | ||
103 | /* | |
104 | * bdrv_graph_wrlock: | |
105 | * Start an exclusive write operation to modify the graph. This means we are | |
106 | * adding or removing an edge or a node in the block layer graph. Nobody else | |
107 | * is allowed to access the graph. | |
108 | * | |
109 | * Must only be called from outside bdrv_graph_co_rdlock. | |
110 | * | |
111 | * The wrlock can only be taken from the main loop, with BQL held, as only the | |
112 | * main loop is allowed to modify the graph. | |
113 | * | |
31b2ddfe KW |
114 | * If @bs is non-NULL, its AioContext is temporarily released. |
115 | * | |
aead9dc9 | 116 | * This function polls. Callers must not hold the lock of any AioContext other |
31b2ddfe | 117 | * than the current one and the one of @bs. |
aead9dc9 | 118 | */ |
e6e964b8 KW |
119 | void no_coroutine_fn TSA_ACQUIRE(graph_lock) TSA_NO_TSA |
120 | bdrv_graph_wrlock(BlockDriverState *bs); | |
aead9dc9 PB |
121 | |
122 | /* | |
123 | * bdrv_graph_wrunlock: | |
124 | * Write finished, reset global has_writer to 0 and restart | |
125 | * all readers that are waiting. | |
126 | */ | |
4002ffdc | 127 | void bdrv_graph_wrunlock(void) TSA_RELEASE(graph_lock) TSA_NO_TSA; |
aead9dc9 PB |
128 | |
129 | /* | |
130 | * bdrv_graph_co_rdlock: | |
131 | * Read the bs graph. This usually means traversing all nodes in | |
132 | * the graph, therefore it can't happen while another thread is | |
133 | * modifying it. | |
134 | * Increases the reader counter of the current aiocontext, | |
135 | * and if has_writer is set, it means that the writer is modifying | |
136 | * the graph, therefore wait in a coroutine queue. | |
137 | * The writer will then wake this coroutine once it is done. | |
138 | * | |
139 | * This lock should be taken from Iothreads (IO_CODE() class of functions) | |
140 | * because it signals the writer that there are some | |
141 | * readers currently running, or waits until the current | |
142 | * write is finished before continuing. | |
143 | * Calling this function from the Main Loop with BQL held | |
144 | * is not necessary, since the Main Loop itself is the only | |
145 | * writer, thus won't be able to read and write at the same time. | |
146 | * The only exception to that is when we can't take the lock in the | |
147 | * function/coroutine itself, and need to delegate the caller (usually main | |
148 | * loop) to take it and wait that the coroutine ends, so that | |
149 | * we always signal that a reader is running. | |
150 | */ | |
4002ffdc KW |
151 | void coroutine_fn TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA |
152 | bdrv_graph_co_rdlock(void); | |
aead9dc9 PB |
153 | |
154 | /* | |
155 | * bdrv_graph_rdunlock: | |
156 | * Read terminated, decrease the count of readers in the current aiocontext. | |
157 | * If the writer is waiting for reads to finish (has_writer == 1), signal | |
158 | * the writer that we are done via aio_wait_kick() to let it continue. | |
159 | */ | |
4002ffdc KW |
160 | void coroutine_fn TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA |
161 | bdrv_graph_co_rdunlock(void); | |
aead9dc9 PB |
162 | |
163 | /* | |
164 | * bdrv_graph_rd{un}lock_main_loop: | |
165 | * Just a placeholder to mark where the graph rdlock should be taken | |
166 | * in the main loop. It is just asserting that we are not | |
167 | * in a coroutine and in GLOBAL_STATE_CODE. | |
168 | */ | |
4002ffdc KW |
169 | void TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA |
170 | bdrv_graph_rdlock_main_loop(void); | |
171 | ||
172 | void TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA | |
173 | bdrv_graph_rdunlock_main_loop(void); | |
aead9dc9 | 174 | |
3f35f82e EGE |
175 | /* |
176 | * assert_bdrv_graph_readable: | |
177 | * Make sure that the reader is either the main loop, | |
178 | * or there is at least a reader helding the rdlock. | |
179 | * In this way an incoming writer is aware of the read and waits. | |
180 | */ | |
303de47b | 181 | void GRAPH_RDLOCK assert_bdrv_graph_readable(void); |
3f35f82e EGE |
182 | |
183 | /* | |
184 | * assert_bdrv_graph_writable: | |
185 | * Make sure that the writer is the main loop and has set @has_writer, | |
186 | * so that incoming readers will pause. | |
187 | */ | |
303de47b | 188 | void GRAPH_WRLOCK assert_bdrv_graph_writable(void); |
3f35f82e | 189 | |
4002ffdc KW |
190 | /* |
191 | * Calling this function tells TSA that we know that the lock is effectively | |
192 | * taken even though we cannot prove it (yet) with GRAPH_RDLOCK. This can be | |
193 | * useful in intermediate stages of a conversion to using the GRAPH_RDLOCK | |
194 | * macro. | |
195 | */ | |
196 | static inline void TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA | |
197 | assume_graph_lock(void) | |
198 | { | |
199 | } | |
200 | ||
8aa77000 EGE |
201 | typedef struct GraphLockable { } GraphLockable; |
202 | ||
203 | /* | |
204 | * In C, compound literals have the lifetime of an automatic variable. | |
205 | * In C++ it would be different, but then C++ wouldn't need QemuLockable | |
206 | * either... | |
207 | */ | |
208 | #define GML_OBJ_() (&(GraphLockable) { }) | |
209 | ||
4002ffdc | 210 | /* |
4ee1f854 | 211 | * This is not marked as TSA_ACQUIRE_SHARED() because TSA doesn't understand the |
4002ffdc | 212 | * cleanup attribute and would therefore complain that the graph is never |
4ee1f854 KW |
213 | * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that |
214 | * we hold the lock while unlocking is left unchecked. | |
4002ffdc | 215 | */ |
4ee1f854 | 216 | static inline GraphLockable * TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA coroutine_fn |
4002ffdc | 217 | graph_lockable_auto_lock(GraphLockable *x) |
8aa77000 EGE |
218 | { |
219 | bdrv_graph_co_rdlock(); | |
220 | return x; | |
221 | } | |
222 | ||
17ac39c3 | 223 | static inline void TSA_NO_TSA coroutine_fn |
4002ffdc | 224 | graph_lockable_auto_unlock(GraphLockable *x) |
8aa77000 EGE |
225 | { |
226 | bdrv_graph_co_rdunlock(); | |
227 | } | |
228 | ||
229 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockable, graph_lockable_auto_unlock) | |
230 | ||
231 | #define WITH_GRAPH_RDLOCK_GUARD_(var) \ | |
232 | for (g_autoptr(GraphLockable) var = graph_lockable_auto_lock(GML_OBJ_()); \ | |
233 | var; \ | |
234 | graph_lockable_auto_unlock(var), var = NULL) | |
235 | ||
236 | #define WITH_GRAPH_RDLOCK_GUARD() \ | |
237 | WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) | |
238 | ||
239 | #define GRAPH_RDLOCK_GUARD(x) \ | |
240 | g_autoptr(GraphLockable) \ | |
241 | glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ | |
242 | graph_lockable_auto_lock(GML_OBJ_()) | |
243 | ||
244 | ||
245 | typedef struct GraphLockableMainloop { } GraphLockableMainloop; | |
246 | ||
247 | /* | |
248 | * In C, compound literals have the lifetime of an automatic variable. | |
249 | * In C++ it would be different, but then C++ wouldn't need QemuLockable | |
250 | * either... | |
251 | */ | |
252 | #define GMLML_OBJ_() (&(GraphLockableMainloop) { }) | |
253 | ||
4002ffdc | 254 | /* |
4ee1f854 | 255 | * This is not marked as TSA_ACQUIRE_SHARED() because TSA doesn't understand the |
4002ffdc | 256 | * cleanup attribute and would therefore complain that the graph is never |
4ee1f854 KW |
257 | * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that |
258 | * we hold the lock while unlocking is left unchecked. | |
4002ffdc | 259 | */ |
4ee1f854 | 260 | static inline GraphLockableMainloop * TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA |
8aa77000 EGE |
261 | graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x) |
262 | { | |
263 | bdrv_graph_rdlock_main_loop(); | |
264 | return x; | |
265 | } | |
266 | ||
4002ffdc | 267 | static inline void TSA_NO_TSA |
8aa77000 EGE |
268 | graph_lockable_auto_unlock_mainloop(GraphLockableMainloop *x) |
269 | { | |
270 | bdrv_graph_rdunlock_main_loop(); | |
271 | } | |
272 | ||
273 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockableMainloop, | |
274 | graph_lockable_auto_unlock_mainloop) | |
275 | ||
276 | #define GRAPH_RDLOCK_GUARD_MAINLOOP(x) \ | |
277 | g_autoptr(GraphLockableMainloop) \ | |
278 | glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ | |
279 | graph_lockable_auto_lock_mainloop(GMLML_OBJ_()) | |
280 | ||
aead9dc9 PB |
281 | #endif /* GRAPH_LOCK_H */ |
282 |