1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "qemu/osdep.h"
26
27#include "block/nbd.h"
28
29#include "qapi/qapi-visit-sockets.h"
30#include "qapi/clone-visitor.h"
31
32struct NBDClientConnection {
33
34 SocketAddress *saddr;
35 QCryptoTLSCreds *tlscreds;
36 NBDExportInfo initial_info;
37 bool do_negotiation;
38 bool do_retry;
39
40 QemuMutex mutex;
41
42
43
44
45
46
47
48
49 NBDExportInfo updated_info;
50 QIOChannelSocket *sioc;
51 QIOChannel *ioc;
52 Error *err;
53
54
55 bool running;
56 bool detached;
57
58
59
60
61
62 Coroutine *wait_co;
63};
64
65
66
67
68
69void nbd_client_connection_enable_retry(NBDClientConnection *conn)
70{
71 conn->do_retry = true;
72}
73
74NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr,
75 bool do_negotiation,
76 const char *export_name,
77 const char *x_dirty_bitmap,
78 QCryptoTLSCreds *tlscreds)
79{
80 NBDClientConnection *conn = g_new(NBDClientConnection, 1);
81
82 object_ref(OBJECT(tlscreds));
83 *conn = (NBDClientConnection) {
84 .saddr = QAPI_CLONE(SocketAddress, saddr),
85 .tlscreds = tlscreds,
86 .do_negotiation = do_negotiation,
87
88 .initial_info.request_sizes = true,
89 .initial_info.structured_reply = true,
90 .initial_info.base_allocation = true,
91 .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap),
92 .initial_info.name = g_strdup(export_name ?: "")
93 };
94
95 qemu_mutex_init(&conn->mutex);
96
97 return conn;
98}
99
100static void nbd_client_connection_do_free(NBDClientConnection *conn)
101{
102 if (conn->sioc) {
103 qio_channel_close(QIO_CHANNEL(conn->sioc), NULL);
104 object_unref(OBJECT(conn->sioc));
105 }
106 error_free(conn->err);
107 qapi_free_SocketAddress(conn->saddr);
108 object_unref(OBJECT(conn->tlscreds));
109 g_free(conn->initial_info.x_dirty_bitmap);
110 g_free(conn->initial_info.name);
111 g_free(conn);
112}
113
114
115
116
117
118
119static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr,
120 NBDExportInfo *info, QCryptoTLSCreds *tlscreds,
121 QIOChannel **outioc, Error **errp)
122{
123 int ret;
124
125 if (outioc) {
126 *outioc = NULL;
127 }
128
129 ret = qio_channel_socket_connect_sync(sioc, addr, errp);
130 if (ret < 0) {
131 return ret;
132 }
133
134 qio_channel_set_delay(QIO_CHANNEL(sioc), false);
135
136 if (!info) {
137 return 0;
138 }
139
140 ret = nbd_receive_negotiate(NULL, QIO_CHANNEL(sioc), tlscreds,
141 tlscreds ? addr->u.inet.host : NULL,
142 outioc, info, errp);
143 if (ret < 0) {
144
145
146
147
148
149 if (outioc && *outioc) {
150 qio_channel_close(QIO_CHANNEL(*outioc), NULL);
151 object_unref(OBJECT(*outioc));
152 *outioc = NULL;
153 } else {
154 qio_channel_close(QIO_CHANNEL(sioc), NULL);
155 }
156
157 return ret;
158 }
159
160 return 0;
161}
162
163static void *connect_thread_func(void *opaque)
164{
165 NBDClientConnection *conn = opaque;
166 int ret;
167 bool do_free;
168 uint64_t timeout = 1;
169 uint64_t max_timeout = 16;
170
171 qemu_mutex_lock(&conn->mutex);
172 while (!conn->detached) {
173 assert(!conn->sioc);
174 conn->sioc = qio_channel_socket_new();
175
176 qemu_mutex_unlock(&conn->mutex);
177
178 error_free(conn->err);
179 conn->err = NULL;
180 conn->updated_info = conn->initial_info;
181
182 ret = nbd_connect(conn->sioc, conn->saddr,
183 conn->do_negotiation ? &conn->updated_info : NULL,
184 conn->tlscreds, &conn->ioc, &conn->err);
185
186
187
188
189
190
191
192 conn->updated_info.x_dirty_bitmap = NULL;
193 conn->updated_info.name = NULL;
194
195 qemu_mutex_lock(&conn->mutex);
196
197 if (ret < 0) {
198 object_unref(OBJECT(conn->sioc));
199 conn->sioc = NULL;
200 if (conn->do_retry && !conn->detached) {
201 qemu_mutex_unlock(&conn->mutex);
202
203 sleep(timeout);
204 if (timeout < max_timeout) {
205 timeout *= 2;
206 }
207
208 qemu_mutex_lock(&conn->mutex);
209 continue;
210 }
211 }
212
213 break;
214 }
215
216
217
218 assert(conn->running);
219 conn->running = false;
220 if (conn->wait_co) {
221 aio_co_wake(conn->wait_co);
222 conn->wait_co = NULL;
223 }
224 do_free = conn->detached;
225
226 qemu_mutex_unlock(&conn->mutex);
227
228 if (do_free) {
229 nbd_client_connection_do_free(conn);
230 }
231
232 return NULL;
233}
234
235void nbd_client_connection_release(NBDClientConnection *conn)
236{
237 bool do_free = false;
238
239 if (!conn) {
240 return;
241 }
242
243 WITH_QEMU_LOCK_GUARD(&conn->mutex) {
244 assert(!conn->detached);
245 if (conn->running) {
246 conn->detached = true;
247 } else {
248 do_free = true;
249 }
250 if (conn->sioc) {
251 qio_channel_shutdown(QIO_CHANNEL(conn->sioc),
252 QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
253 }
254 }
255
256 if (do_free) {
257 nbd_client_connection_do_free(conn);
258 }
259}
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277QIOChannel *coroutine_fn
278nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info,
279 bool blocking, Error **errp)
280{
281 QemuThread thread;
282
283 if (conn->do_negotiation) {
284 assert(info);
285 }
286
287 WITH_QEMU_LOCK_GUARD(&conn->mutex) {
288
289
290
291
292 assert(!conn->wait_co);
293
294 if (!conn->running) {
295 if (conn->sioc) {
296
297 if (conn->do_negotiation) {
298 memcpy(info, &conn->updated_info, sizeof(*info));
299 if (conn->ioc) {
300
301 object_unref(OBJECT(conn->sioc));
302 conn->sioc = NULL;
303
304 return g_steal_pointer(&conn->ioc);
305 }
306 }
307
308 assert(!conn->ioc);
309
310 return QIO_CHANNEL(g_steal_pointer(&conn->sioc));
311 }
312
313 conn->running = true;
314 error_free(conn->err);
315 conn->err = NULL;
316 qemu_thread_create(&thread, "nbd-connect",
317 connect_thread_func, conn, QEMU_THREAD_DETACHED);
318 }
319
320 if (!blocking) {
321 return NULL;
322 }
323
324 conn->wait_co = qemu_coroutine_self();
325 }
326
327
328
329
330
331 qemu_coroutine_yield();
332
333 WITH_QEMU_LOCK_GUARD(&conn->mutex) {
334 if (conn->running) {
335
336
337
338
339
340
341 error_setg(errp, "Connection attempt cancelled by other operation");
342 return NULL;
343 } else {
344 error_propagate(errp, conn->err);
345 conn->err = NULL;
346 if (!conn->sioc) {
347 return NULL;
348 }
349 if (conn->do_negotiation) {
350 memcpy(info, &conn->updated_info, sizeof(*info));
351 if (conn->ioc) {
352
353 object_unref(OBJECT(conn->sioc));
354 conn->sioc = NULL;
355
356 return g_steal_pointer(&conn->ioc);
357 }
358 }
359
360 assert(!conn->ioc);
361
362 return QIO_CHANNEL(g_steal_pointer(&conn->sioc));
363 }
364 }
365
366 abort();
367}
368
369
370
371
372
373
374
375
376
377void nbd_co_establish_connection_cancel(NBDClientConnection *conn)
378{
379 Coroutine *wait_co;
380
381 WITH_QEMU_LOCK_GUARD(&conn->mutex) {
382 wait_co = g_steal_pointer(&conn->wait_co);
383 }
384
385 if (wait_co) {
386 aio_co_wake(wait_co);
387 }
388}
389