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