]>
Commit | Line | Data |
---|---|---|
2302c1ca MAL |
1 | /* |
2 | * QEMU Block driver for NBD | |
3 | * | |
b626b51a | 4 | * Copyright (C) 2016 Red Hat, Inc. |
2302c1ca MAL |
5 | * Copyright (C) 2008 Bull S.A.S. |
6 | * Author: Laurent Vivier <Laurent.Vivier@bull.net> | |
7 | * | |
8 | * Some parts: | |
9 | * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws> | |
10 | * | |
11 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
12 | * of this software and associated documentation files (the "Software"), to deal | |
13 | * in the Software without restriction, including without limitation the rights | |
14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
15 | * copies of the Software, and to permit persons to whom the Software is | |
16 | * furnished to do so, subject to the following conditions: | |
17 | * | |
18 | * The above copyright notice and this permission notice shall be included in | |
19 | * all copies or substantial portions of the Software. | |
20 | * | |
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
24 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
27 | * THE SOFTWARE. | |
28 | */ | |
29 | ||
80c71a24 | 30 | #include "qemu/osdep.h" |
be41c100 | 31 | #include "qapi/error.h" |
2302c1ca | 32 | #include "nbd-client.h" |
2302c1ca MAL |
33 | |
34 | #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs)) | |
35 | #define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs)) | |
36 | ||
a12a712a | 37 | static void nbd_recv_coroutines_enter_all(NBDClientSession *s) |
69152c09 MAL |
38 | { |
39 | int i; | |
40 | ||
41 | for (i = 0; i < MAX_NBD_REQUESTS; i++) { | |
42 | if (s->recv_coroutine[i]) { | |
a12a712a | 43 | aio_co_wake(s->recv_coroutine[i]); |
69152c09 MAL |
44 | } |
45 | } | |
46 | } | |
47 | ||
f53a829b | 48 | static void nbd_teardown_connection(BlockDriverState *bs) |
4a41a2d6 | 49 | { |
10676b81 | 50 | NBDClientSession *client = nbd_get_client_session(bs); |
f53a829b | 51 | |
064097d9 DB |
52 | if (!client->ioc) { /* Already closed */ |
53 | return; | |
54 | } | |
55 | ||
4a41a2d6 | 56 | /* finish any pending coroutines */ |
064097d9 DB |
57 | qio_channel_shutdown(client->ioc, |
58 | QIO_CHANNEL_SHUTDOWN_BOTH, | |
59 | NULL); | |
a12a712a | 60 | BDRV_POLL_WHILE(bs, client->read_reply_co); |
4a41a2d6 | 61 | |
f53a829b | 62 | nbd_client_detach_aio_context(bs); |
064097d9 DB |
63 | object_unref(OBJECT(client->sioc)); |
64 | client->sioc = NULL; | |
65 | object_unref(OBJECT(client->ioc)); | |
66 | client->ioc = NULL; | |
4a41a2d6 SH |
67 | } |
68 | ||
ff82911c | 69 | static coroutine_fn void nbd_read_reply_entry(void *opaque) |
2302c1ca | 70 | { |
ff82911c | 71 | NBDClientSession *s = opaque; |
2302c1ca | 72 | uint64_t i; |
d0a18013 | 73 | int ret = 0; |
be41c100 | 74 | Error *local_err = NULL; |
2302c1ca | 75 | |
72b6ffc7 | 76 | while (!s->quit) { |
ff82911c | 77 | assert(s->reply.handle == 0); |
be41c100 VSO |
78 | ret = nbd_receive_reply(s->ioc, &s->reply, &local_err); |
79 | if (ret < 0) { | |
80 | error_report_err(local_err); | |
81 | } | |
a12a712a | 82 | if (ret <= 0) { |
ff82911c | 83 | break; |
2302c1ca | 84 | } |
2302c1ca | 85 | |
ff82911c PB |
86 | /* There's no need for a mutex on the receive side, because the |
87 | * handler acts as a synchronization point and ensures that only | |
88 | * one coroutine is called until the reply finishes. | |
89 | */ | |
90 | i = HANDLE_TO_INDEX(s, s->reply.handle); | |
91 | if (i >= MAX_NBD_REQUESTS || !s->recv_coroutine[i]) { | |
92 | break; | |
93 | } | |
2302c1ca | 94 | |
ff82911c PB |
95 | /* We're woken up by the recv_coroutine itself. Note that there |
96 | * is no race between yielding and reentering read_reply_co. This | |
97 | * is because: | |
98 | * | |
99 | * - if recv_coroutine[i] runs on the same AioContext, it is only | |
100 | * entered after we yield | |
101 | * | |
102 | * - if recv_coroutine[i] runs on a different AioContext, reentering | |
103 | * read_reply_co happens through a bottom half, which can only | |
104 | * run after we yield. | |
105 | */ | |
106 | aio_co_wake(s->recv_coroutine[i]); | |
107 | qemu_coroutine_yield(); | |
2302c1ca | 108 | } |
a12a712a | 109 | |
72b6ffc7 EB |
110 | if (ret < 0) { |
111 | s->quit = true; | |
112 | } | |
a12a712a | 113 | nbd_recv_coroutines_enter_all(s); |
ff82911c | 114 | s->read_reply_co = NULL; |
2302c1ca MAL |
115 | } |
116 | ||
f53a829b | 117 | static int nbd_co_send_request(BlockDriverState *bs, |
ed2dd912 | 118 | NBDRequest *request, |
1e2a77a8 | 119 | QEMUIOVector *qiov) |
2302c1ca | 120 | { |
10676b81 | 121 | NBDClientSession *s = nbd_get_client_session(bs); |
141cabe6 | 122 | int rc, ret, i; |
2302c1ca MAL |
123 | |
124 | qemu_co_mutex_lock(&s->send_mutex); | |
6bdcc018 PB |
125 | while (s->in_flight == MAX_NBD_REQUESTS) { |
126 | qemu_co_queue_wait(&s->free_sema, &s->send_mutex); | |
127 | } | |
128 | s->in_flight++; | |
141cabe6 BW |
129 | |
130 | for (i = 0; i < MAX_NBD_REQUESTS; i++) { | |
131 | if (s->recv_coroutine[i] == NULL) { | |
132 | s->recv_coroutine[i] = qemu_coroutine_self(); | |
133 | break; | |
134 | } | |
135 | } | |
136 | ||
1c778ef7 | 137 | g_assert(qemu_in_coroutine()); |
141cabe6 BW |
138 | assert(i < MAX_NBD_REQUESTS); |
139 | request->handle = INDEX_TO_HANDLE(s, i); | |
064097d9 | 140 | |
72b6ffc7 EB |
141 | if (s->quit) { |
142 | qemu_co_mutex_unlock(&s->send_mutex); | |
143 | return -EIO; | |
144 | } | |
064097d9 DB |
145 | if (!s->ioc) { |
146 | qemu_co_mutex_unlock(&s->send_mutex); | |
147 | return -EPIPE; | |
148 | } | |
149 | ||
2302c1ca | 150 | if (qiov) { |
064097d9 | 151 | qio_channel_set_cork(s->ioc, true); |
1c778ef7 | 152 | rc = nbd_send_request(s->ioc, request); |
72b6ffc7 | 153 | if (rc >= 0 && !s->quit) { |
d1fdf257 VSO |
154 | ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, false, |
155 | NULL); | |
2302c1ca MAL |
156 | if (ret != request->len) { |
157 | rc = -EIO; | |
158 | } | |
159 | } | |
064097d9 | 160 | qio_channel_set_cork(s->ioc, false); |
2302c1ca | 161 | } else { |
1c778ef7 | 162 | rc = nbd_send_request(s->ioc, request); |
2302c1ca | 163 | } |
72b6ffc7 EB |
164 | if (rc < 0) { |
165 | s->quit = true; | |
166 | } | |
2302c1ca MAL |
167 | qemu_co_mutex_unlock(&s->send_mutex); |
168 | return rc; | |
169 | } | |
170 | ||
10676b81 | 171 | static void nbd_co_receive_reply(NBDClientSession *s, |
ed2dd912 EB |
172 | NBDRequest *request, |
173 | NBDReply *reply, | |
1e2a77a8 | 174 | QEMUIOVector *qiov) |
2302c1ca MAL |
175 | { |
176 | int ret; | |
177 | ||
ff82911c | 178 | /* Wait until we're woken up by nbd_read_reply_entry. */ |
2302c1ca MAL |
179 | qemu_coroutine_yield(); |
180 | *reply = s->reply; | |
72b6ffc7 | 181 | if (reply->handle != request->handle || !s->ioc || s->quit) { |
2302c1ca MAL |
182 | reply->error = EIO; |
183 | } else { | |
184 | if (qiov && reply->error == 0) { | |
d1fdf257 VSO |
185 | ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, true, |
186 | NULL); | |
2302c1ca MAL |
187 | if (ret != request->len) { |
188 | reply->error = EIO; | |
189 | } | |
190 | } | |
191 | ||
192 | /* Tell the read handler to read another header. */ | |
193 | s->reply.handle = 0; | |
194 | } | |
195 | } | |
196 | ||
ff82911c | 197 | static void nbd_coroutine_end(BlockDriverState *bs, |
ed2dd912 | 198 | NBDRequest *request) |
2302c1ca | 199 | { |
ff82911c | 200 | NBDClientSession *s = nbd_get_client_session(bs); |
2302c1ca | 201 | int i = HANDLE_TO_INDEX(s, request->handle); |
ff82911c | 202 | |
2302c1ca | 203 | s->recv_coroutine[i] = NULL; |
ff82911c PB |
204 | |
205 | /* Kick the read_reply_co to get the next reply. */ | |
206 | if (s->read_reply_co) { | |
207 | aio_co_wake(s->read_reply_co); | |
2302c1ca | 208 | } |
6bdcc018 PB |
209 | |
210 | qemu_co_mutex_lock(&s->send_mutex); | |
211 | s->in_flight--; | |
212 | qemu_co_queue_next(&s->free_sema); | |
213 | qemu_co_mutex_unlock(&s->send_mutex); | |
2302c1ca MAL |
214 | } |
215 | ||
70c4fb26 EB |
216 | int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, |
217 | uint64_t bytes, QEMUIOVector *qiov, int flags) | |
2302c1ca | 218 | { |
10676b81 | 219 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 | 220 | NBDRequest request = { |
70c4fb26 EB |
221 | .type = NBD_CMD_READ, |
222 | .from = offset, | |
223 | .len = bytes, | |
224 | }; | |
ed2dd912 | 225 | NBDReply reply; |
2302c1ca MAL |
226 | ssize_t ret; |
227 | ||
70c4fb26 EB |
228 | assert(bytes <= NBD_MAX_BUFFER_SIZE); |
229 | assert(!flags); | |
2302c1ca | 230 | |
1e2a77a8 | 231 | ret = nbd_co_send_request(bs, &request, NULL); |
2302c1ca MAL |
232 | if (ret < 0) { |
233 | reply.error = -ret; | |
234 | } else { | |
1e2a77a8 | 235 | nbd_co_receive_reply(client, &request, &reply, qiov); |
2302c1ca | 236 | } |
ff82911c | 237 | nbd_coroutine_end(bs, &request); |
2302c1ca | 238 | return -reply.error; |
2302c1ca MAL |
239 | } |
240 | ||
70c4fb26 EB |
241 | int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, |
242 | uint64_t bytes, QEMUIOVector *qiov, int flags) | |
2302c1ca | 243 | { |
10676b81 | 244 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 | 245 | NBDRequest request = { |
70c4fb26 EB |
246 | .type = NBD_CMD_WRITE, |
247 | .from = offset, | |
248 | .len = bytes, | |
249 | }; | |
ed2dd912 | 250 | NBDReply reply; |
2302c1ca MAL |
251 | ssize_t ret; |
252 | ||
52a46505 | 253 | if (flags & BDRV_REQ_FUA) { |
004a89fc | 254 | assert(client->info.flags & NBD_FLAG_SEND_FUA); |
b626b51a | 255 | request.flags |= NBD_CMD_FLAG_FUA; |
2302c1ca MAL |
256 | } |
257 | ||
70c4fb26 | 258 | assert(bytes <= NBD_MAX_BUFFER_SIZE); |
2302c1ca | 259 | |
1e2a77a8 | 260 | ret = nbd_co_send_request(bs, &request, qiov); |
2302c1ca MAL |
261 | if (ret < 0) { |
262 | reply.error = -ret; | |
263 | } else { | |
1e2a77a8 | 264 | nbd_co_receive_reply(client, &request, &reply, NULL); |
2302c1ca | 265 | } |
ff82911c | 266 | nbd_coroutine_end(bs, &request); |
2302c1ca MAL |
267 | return -reply.error; |
268 | } | |
269 | ||
fa778fff | 270 | int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, |
f5a5ca79 | 271 | int bytes, BdrvRequestFlags flags) |
fa778fff EB |
272 | { |
273 | ssize_t ret; | |
274 | NBDClientSession *client = nbd_get_client_session(bs); | |
275 | NBDRequest request = { | |
276 | .type = NBD_CMD_WRITE_ZEROES, | |
277 | .from = offset, | |
f5a5ca79 | 278 | .len = bytes, |
fa778fff EB |
279 | }; |
280 | NBDReply reply; | |
281 | ||
004a89fc | 282 | if (!(client->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) { |
fa778fff EB |
283 | return -ENOTSUP; |
284 | } | |
285 | ||
286 | if (flags & BDRV_REQ_FUA) { | |
004a89fc | 287 | assert(client->info.flags & NBD_FLAG_SEND_FUA); |
fa778fff EB |
288 | request.flags |= NBD_CMD_FLAG_FUA; |
289 | } | |
290 | if (!(flags & BDRV_REQ_MAY_UNMAP)) { | |
291 | request.flags |= NBD_CMD_FLAG_NO_HOLE; | |
292 | } | |
293 | ||
fa778fff EB |
294 | ret = nbd_co_send_request(bs, &request, NULL); |
295 | if (ret < 0) { | |
296 | reply.error = -ret; | |
297 | } else { | |
298 | nbd_co_receive_reply(client, &request, &reply, NULL); | |
299 | } | |
ff82911c | 300 | nbd_coroutine_end(bs, &request); |
fa778fff EB |
301 | return -reply.error; |
302 | } | |
303 | ||
f53a829b | 304 | int nbd_client_co_flush(BlockDriverState *bs) |
2302c1ca | 305 | { |
10676b81 | 306 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 EB |
307 | NBDRequest request = { .type = NBD_CMD_FLUSH }; |
308 | NBDReply reply; | |
2302c1ca MAL |
309 | ssize_t ret; |
310 | ||
004a89fc | 311 | if (!(client->info.flags & NBD_FLAG_SEND_FLUSH)) { |
2302c1ca MAL |
312 | return 0; |
313 | } | |
314 | ||
2302c1ca MAL |
315 | request.from = 0; |
316 | request.len = 0; | |
317 | ||
1e2a77a8 | 318 | ret = nbd_co_send_request(bs, &request, NULL); |
2302c1ca MAL |
319 | if (ret < 0) { |
320 | reply.error = -ret; | |
321 | } else { | |
1e2a77a8 | 322 | nbd_co_receive_reply(client, &request, &reply, NULL); |
2302c1ca | 323 | } |
ff82911c | 324 | nbd_coroutine_end(bs, &request); |
2302c1ca MAL |
325 | return -reply.error; |
326 | } | |
327 | ||
f5a5ca79 | 328 | int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) |
2302c1ca | 329 | { |
10676b81 | 330 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 | 331 | NBDRequest request = { |
447e57c3 EB |
332 | .type = NBD_CMD_TRIM, |
333 | .from = offset, | |
f5a5ca79 | 334 | .len = bytes, |
447e57c3 | 335 | }; |
ed2dd912 | 336 | NBDReply reply; |
2302c1ca MAL |
337 | ssize_t ret; |
338 | ||
004a89fc | 339 | if (!(client->info.flags & NBD_FLAG_SEND_TRIM)) { |
2302c1ca MAL |
340 | return 0; |
341 | } | |
2302c1ca | 342 | |
1e2a77a8 | 343 | ret = nbd_co_send_request(bs, &request, NULL); |
2302c1ca MAL |
344 | if (ret < 0) { |
345 | reply.error = -ret; | |
346 | } else { | |
1e2a77a8 | 347 | nbd_co_receive_reply(client, &request, &reply, NULL); |
2302c1ca | 348 | } |
ff82911c | 349 | nbd_coroutine_end(bs, &request); |
2302c1ca MAL |
350 | return -reply.error; |
351 | ||
352 | } | |
353 | ||
f53a829b | 354 | void nbd_client_detach_aio_context(BlockDriverState *bs) |
69447cd8 | 355 | { |
ff82911c | 356 | NBDClientSession *client = nbd_get_client_session(bs); |
96d06835 | 357 | qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc)); |
69447cd8 SH |
358 | } |
359 | ||
f53a829b HR |
360 | void nbd_client_attach_aio_context(BlockDriverState *bs, |
361 | AioContext *new_context) | |
69447cd8 | 362 | { |
ff82911c | 363 | NBDClientSession *client = nbd_get_client_session(bs); |
96d06835 | 364 | qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context); |
ff82911c | 365 | aio_co_schedule(new_context, client->read_reply_co); |
69447cd8 SH |
366 | } |
367 | ||
f53a829b | 368 | void nbd_client_close(BlockDriverState *bs) |
2302c1ca | 369 | { |
10676b81 | 370 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 | 371 | NBDRequest request = { .type = NBD_CMD_DISC }; |
2302c1ca | 372 | |
064097d9 | 373 | if (client->ioc == NULL) { |
4a41a2d6 SH |
374 | return; |
375 | } | |
376 | ||
1c778ef7 | 377 | nbd_send_request(client->ioc, &request); |
5ad283eb | 378 | |
f53a829b | 379 | nbd_teardown_connection(bs); |
2302c1ca MAL |
380 | } |
381 | ||
75822a12 DB |
382 | int nbd_client_init(BlockDriverState *bs, |
383 | QIOChannelSocket *sioc, | |
384 | const char *export, | |
385 | QCryptoTLSCreds *tlscreds, | |
386 | const char *hostname, | |
387 | Error **errp) | |
2302c1ca | 388 | { |
10676b81 | 389 | NBDClientSession *client = nbd_get_client_session(bs); |
2302c1ca MAL |
390 | int ret; |
391 | ||
392 | /* NBD handshake */ | |
e2bc625f | 393 | logout("session init %s\n", export); |
064097d9 DB |
394 | qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); |
395 | ||
081dd1fe | 396 | client->info.request_sizes = true; |
1c778ef7 | 397 | ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, |
75822a12 | 398 | tlscreds, hostname, |
004a89fc | 399 | &client->ioc, &client->info, errp); |
2302c1ca MAL |
400 | if (ret < 0) { |
401 | logout("Failed to negotiate with the NBD server\n"); | |
2302c1ca MAL |
402 | return ret; |
403 | } | |
004a89fc | 404 | if (client->info.flags & NBD_FLAG_SEND_FUA) { |
4df863f3 | 405 | bs->supported_write_flags = BDRV_REQ_FUA; |
169407e1 EB |
406 | bs->supported_zero_flags |= BDRV_REQ_FUA; |
407 | } | |
004a89fc | 408 | if (client->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) { |
169407e1 | 409 | bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; |
4df863f3 | 410 | } |
081dd1fe EB |
411 | if (client->info.min_block > bs->bl.request_alignment) { |
412 | bs->bl.request_alignment = client->info.min_block; | |
413 | } | |
2302c1ca MAL |
414 | |
415 | qemu_co_mutex_init(&client->send_mutex); | |
9bc9732f | 416 | qemu_co_queue_init(&client->free_sema); |
064097d9 DB |
417 | client->sioc = sioc; |
418 | object_ref(OBJECT(client->sioc)); | |
f95910fe DB |
419 | |
420 | if (!client->ioc) { | |
421 | client->ioc = QIO_CHANNEL(sioc); | |
422 | object_ref(OBJECT(client->ioc)); | |
423 | } | |
2302c1ca MAL |
424 | |
425 | /* Now that we're connected, set the socket to be non-blocking and | |
426 | * kick the reply mechanism. */ | |
064097d9 | 427 | qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); |
ff82911c | 428 | client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client); |
f53a829b | 429 | nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); |
2302c1ca MAL |
430 | |
431 | logout("Established connection with NBD server\n"); | |
432 | return 0; | |
433 | } |