]>
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 <[email protected]> | |
7 | * | |
8 | * Some parts: | |
9 | * Copyright (C) 2007 Anthony Liguori <[email protected]> | |
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 MAL |
72 | uint64_t i; |
73 | int ret; | |
be41c100 | 74 | Error *local_err = NULL; |
2302c1ca | 75 | |
ff82911c PB |
76 | for (;;) { |
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 PB |
109 | |
110 | nbd_recv_coroutines_enter_all(s); | |
ff82911c | 111 | s->read_reply_co = NULL; |
2302c1ca MAL |
112 | } |
113 | ||
f53a829b | 114 | static int nbd_co_send_request(BlockDriverState *bs, |
ed2dd912 | 115 | NBDRequest *request, |
1e2a77a8 | 116 | QEMUIOVector *qiov) |
2302c1ca | 117 | { |
10676b81 | 118 | NBDClientSession *s = nbd_get_client_session(bs); |
141cabe6 | 119 | int rc, ret, i; |
2302c1ca MAL |
120 | |
121 | qemu_co_mutex_lock(&s->send_mutex); | |
6bdcc018 PB |
122 | while (s->in_flight == MAX_NBD_REQUESTS) { |
123 | qemu_co_queue_wait(&s->free_sema, &s->send_mutex); | |
124 | } | |
125 | s->in_flight++; | |
141cabe6 BW |
126 | |
127 | for (i = 0; i < MAX_NBD_REQUESTS; i++) { | |
128 | if (s->recv_coroutine[i] == NULL) { | |
129 | s->recv_coroutine[i] = qemu_coroutine_self(); | |
130 | break; | |
131 | } | |
132 | } | |
133 | ||
1c778ef7 | 134 | g_assert(qemu_in_coroutine()); |
141cabe6 BW |
135 | assert(i < MAX_NBD_REQUESTS); |
136 | request->handle = INDEX_TO_HANDLE(s, i); | |
064097d9 DB |
137 | |
138 | if (!s->ioc) { | |
139 | qemu_co_mutex_unlock(&s->send_mutex); | |
140 | return -EPIPE; | |
141 | } | |
142 | ||
2302c1ca | 143 | if (qiov) { |
064097d9 | 144 | qio_channel_set_cork(s->ioc, true); |
1c778ef7 | 145 | rc = nbd_send_request(s->ioc, request); |
2302c1ca | 146 | if (rc >= 0) { |
d1fdf257 VSO |
147 | ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, false, |
148 | NULL); | |
2302c1ca MAL |
149 | if (ret != request->len) { |
150 | rc = -EIO; | |
151 | } | |
152 | } | |
064097d9 | 153 | qio_channel_set_cork(s->ioc, false); |
2302c1ca | 154 | } else { |
1c778ef7 | 155 | rc = nbd_send_request(s->ioc, request); |
2302c1ca | 156 | } |
2302c1ca MAL |
157 | qemu_co_mutex_unlock(&s->send_mutex); |
158 | return rc; | |
159 | } | |
160 | ||
10676b81 | 161 | static void nbd_co_receive_reply(NBDClientSession *s, |
ed2dd912 EB |
162 | NBDRequest *request, |
163 | NBDReply *reply, | |
1e2a77a8 | 164 | QEMUIOVector *qiov) |
2302c1ca MAL |
165 | { |
166 | int ret; | |
167 | ||
ff82911c | 168 | /* Wait until we're woken up by nbd_read_reply_entry. */ |
2302c1ca MAL |
169 | qemu_coroutine_yield(); |
170 | *reply = s->reply; | |
064097d9 DB |
171 | if (reply->handle != request->handle || |
172 | !s->ioc) { | |
2302c1ca MAL |
173 | reply->error = EIO; |
174 | } else { | |
175 | if (qiov && reply->error == 0) { | |
d1fdf257 VSO |
176 | ret = nbd_rwv(s->ioc, qiov->iov, qiov->niov, request->len, true, |
177 | NULL); | |
2302c1ca MAL |
178 | if (ret != request->len) { |
179 | reply->error = EIO; | |
180 | } | |
181 | } | |
182 | ||
183 | /* Tell the read handler to read another header. */ | |
184 | s->reply.handle = 0; | |
185 | } | |
186 | } | |
187 | ||
ff82911c | 188 | static void nbd_coroutine_end(BlockDriverState *bs, |
ed2dd912 | 189 | NBDRequest *request) |
2302c1ca | 190 | { |
ff82911c | 191 | NBDClientSession *s = nbd_get_client_session(bs); |
2302c1ca | 192 | int i = HANDLE_TO_INDEX(s, request->handle); |
ff82911c | 193 | |
2302c1ca | 194 | s->recv_coroutine[i] = NULL; |
ff82911c PB |
195 | |
196 | /* Kick the read_reply_co to get the next reply. */ | |
197 | if (s->read_reply_co) { | |
198 | aio_co_wake(s->read_reply_co); | |
2302c1ca | 199 | } |
6bdcc018 PB |
200 | |
201 | qemu_co_mutex_lock(&s->send_mutex); | |
202 | s->in_flight--; | |
203 | qemu_co_queue_next(&s->free_sema); | |
204 | qemu_co_mutex_unlock(&s->send_mutex); | |
2302c1ca MAL |
205 | } |
206 | ||
70c4fb26 EB |
207 | int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, |
208 | uint64_t bytes, QEMUIOVector *qiov, int flags) | |
2302c1ca | 209 | { |
10676b81 | 210 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 | 211 | NBDRequest request = { |
70c4fb26 EB |
212 | .type = NBD_CMD_READ, |
213 | .from = offset, | |
214 | .len = bytes, | |
215 | }; | |
ed2dd912 | 216 | NBDReply reply; |
2302c1ca MAL |
217 | ssize_t ret; |
218 | ||
70c4fb26 EB |
219 | assert(bytes <= NBD_MAX_BUFFER_SIZE); |
220 | assert(!flags); | |
2302c1ca | 221 | |
1e2a77a8 | 222 | ret = nbd_co_send_request(bs, &request, NULL); |
2302c1ca MAL |
223 | if (ret < 0) { |
224 | reply.error = -ret; | |
225 | } else { | |
1e2a77a8 | 226 | nbd_co_receive_reply(client, &request, &reply, qiov); |
2302c1ca | 227 | } |
ff82911c | 228 | nbd_coroutine_end(bs, &request); |
2302c1ca | 229 | return -reply.error; |
2302c1ca MAL |
230 | } |
231 | ||
70c4fb26 EB |
232 | int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, |
233 | uint64_t bytes, QEMUIOVector *qiov, int flags) | |
2302c1ca | 234 | { |
10676b81 | 235 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 | 236 | NBDRequest request = { |
70c4fb26 EB |
237 | .type = NBD_CMD_WRITE, |
238 | .from = offset, | |
239 | .len = bytes, | |
240 | }; | |
ed2dd912 | 241 | NBDReply reply; |
2302c1ca MAL |
242 | ssize_t ret; |
243 | ||
52a46505 EB |
244 | if (flags & BDRV_REQ_FUA) { |
245 | assert(client->nbdflags & NBD_FLAG_SEND_FUA); | |
b626b51a | 246 | request.flags |= NBD_CMD_FLAG_FUA; |
2302c1ca MAL |
247 | } |
248 | ||
70c4fb26 | 249 | assert(bytes <= NBD_MAX_BUFFER_SIZE); |
2302c1ca | 250 | |
1e2a77a8 | 251 | ret = nbd_co_send_request(bs, &request, qiov); |
2302c1ca MAL |
252 | if (ret < 0) { |
253 | reply.error = -ret; | |
254 | } else { | |
1e2a77a8 | 255 | nbd_co_receive_reply(client, &request, &reply, NULL); |
2302c1ca | 256 | } |
ff82911c | 257 | nbd_coroutine_end(bs, &request); |
2302c1ca MAL |
258 | return -reply.error; |
259 | } | |
260 | ||
fa778fff EB |
261 | int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, |
262 | int count, BdrvRequestFlags flags) | |
263 | { | |
264 | ssize_t ret; | |
265 | NBDClientSession *client = nbd_get_client_session(bs); | |
266 | NBDRequest request = { | |
267 | .type = NBD_CMD_WRITE_ZEROES, | |
268 | .from = offset, | |
269 | .len = count, | |
270 | }; | |
271 | NBDReply reply; | |
272 | ||
273 | if (!(client->nbdflags & NBD_FLAG_SEND_WRITE_ZEROES)) { | |
274 | return -ENOTSUP; | |
275 | } | |
276 | ||
277 | if (flags & BDRV_REQ_FUA) { | |
278 | assert(client->nbdflags & NBD_FLAG_SEND_FUA); | |
279 | request.flags |= NBD_CMD_FLAG_FUA; | |
280 | } | |
281 | if (!(flags & BDRV_REQ_MAY_UNMAP)) { | |
282 | request.flags |= NBD_CMD_FLAG_NO_HOLE; | |
283 | } | |
284 | ||
fa778fff EB |
285 | ret = nbd_co_send_request(bs, &request, NULL); |
286 | if (ret < 0) { | |
287 | reply.error = -ret; | |
288 | } else { | |
289 | nbd_co_receive_reply(client, &request, &reply, NULL); | |
290 | } | |
ff82911c | 291 | nbd_coroutine_end(bs, &request); |
fa778fff EB |
292 | return -reply.error; |
293 | } | |
294 | ||
f53a829b | 295 | int nbd_client_co_flush(BlockDriverState *bs) |
2302c1ca | 296 | { |
10676b81 | 297 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 EB |
298 | NBDRequest request = { .type = NBD_CMD_FLUSH }; |
299 | NBDReply reply; | |
2302c1ca MAL |
300 | ssize_t ret; |
301 | ||
302 | if (!(client->nbdflags & NBD_FLAG_SEND_FLUSH)) { | |
303 | return 0; | |
304 | } | |
305 | ||
2302c1ca MAL |
306 | request.from = 0; |
307 | request.len = 0; | |
308 | ||
1e2a77a8 | 309 | ret = nbd_co_send_request(bs, &request, NULL); |
2302c1ca MAL |
310 | if (ret < 0) { |
311 | reply.error = -ret; | |
312 | } else { | |
1e2a77a8 | 313 | nbd_co_receive_reply(client, &request, &reply, NULL); |
2302c1ca | 314 | } |
ff82911c | 315 | nbd_coroutine_end(bs, &request); |
2302c1ca MAL |
316 | return -reply.error; |
317 | } | |
318 | ||
447e57c3 | 319 | int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) |
2302c1ca | 320 | { |
10676b81 | 321 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 | 322 | NBDRequest request = { |
447e57c3 EB |
323 | .type = NBD_CMD_TRIM, |
324 | .from = offset, | |
325 | .len = count, | |
326 | }; | |
ed2dd912 | 327 | NBDReply reply; |
2302c1ca MAL |
328 | ssize_t ret; |
329 | ||
330 | if (!(client->nbdflags & NBD_FLAG_SEND_TRIM)) { | |
331 | return 0; | |
332 | } | |
2302c1ca | 333 | |
1e2a77a8 | 334 | ret = nbd_co_send_request(bs, &request, NULL); |
2302c1ca MAL |
335 | if (ret < 0) { |
336 | reply.error = -ret; | |
337 | } else { | |
1e2a77a8 | 338 | nbd_co_receive_reply(client, &request, &reply, NULL); |
2302c1ca | 339 | } |
ff82911c | 340 | nbd_coroutine_end(bs, &request); |
2302c1ca MAL |
341 | return -reply.error; |
342 | ||
343 | } | |
344 | ||
f53a829b | 345 | void nbd_client_detach_aio_context(BlockDriverState *bs) |
69447cd8 | 346 | { |
ff82911c PB |
347 | NBDClientSession *client = nbd_get_client_session(bs); |
348 | qio_channel_detach_aio_context(QIO_CHANNEL(client->sioc)); | |
69447cd8 SH |
349 | } |
350 | ||
f53a829b HR |
351 | void nbd_client_attach_aio_context(BlockDriverState *bs, |
352 | AioContext *new_context) | |
69447cd8 | 353 | { |
ff82911c PB |
354 | NBDClientSession *client = nbd_get_client_session(bs); |
355 | qio_channel_attach_aio_context(QIO_CHANNEL(client->sioc), new_context); | |
356 | aio_co_schedule(new_context, client->read_reply_co); | |
69447cd8 SH |
357 | } |
358 | ||
f53a829b | 359 | void nbd_client_close(BlockDriverState *bs) |
2302c1ca | 360 | { |
10676b81 | 361 | NBDClientSession *client = nbd_get_client_session(bs); |
ed2dd912 | 362 | NBDRequest request = { .type = NBD_CMD_DISC }; |
2302c1ca | 363 | |
064097d9 | 364 | if (client->ioc == NULL) { |
4a41a2d6 SH |
365 | return; |
366 | } | |
367 | ||
1c778ef7 | 368 | nbd_send_request(client->ioc, &request); |
5ad283eb | 369 | |
f53a829b | 370 | nbd_teardown_connection(bs); |
2302c1ca MAL |
371 | } |
372 | ||
75822a12 DB |
373 | int nbd_client_init(BlockDriverState *bs, |
374 | QIOChannelSocket *sioc, | |
375 | const char *export, | |
376 | QCryptoTLSCreds *tlscreds, | |
377 | const char *hostname, | |
378 | Error **errp) | |
2302c1ca | 379 | { |
10676b81 | 380 | NBDClientSession *client = nbd_get_client_session(bs); |
2302c1ca MAL |
381 | int ret; |
382 | ||
383 | /* NBD handshake */ | |
e2bc625f | 384 | logout("session init %s\n", export); |
064097d9 DB |
385 | qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); |
386 | ||
1c778ef7 | 387 | ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, |
f95910fe | 388 | &client->nbdflags, |
75822a12 | 389 | tlscreds, hostname, |
f95910fe DB |
390 | &client->ioc, |
391 | &client->size, errp); | |
2302c1ca MAL |
392 | if (ret < 0) { |
393 | logout("Failed to negotiate with the NBD server\n"); | |
2302c1ca MAL |
394 | return ret; |
395 | } | |
4df863f3 EB |
396 | if (client->nbdflags & NBD_FLAG_SEND_FUA) { |
397 | bs->supported_write_flags = BDRV_REQ_FUA; | |
169407e1 EB |
398 | bs->supported_zero_flags |= BDRV_REQ_FUA; |
399 | } | |
400 | if (client->nbdflags & NBD_FLAG_SEND_WRITE_ZEROES) { | |
401 | bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; | |
4df863f3 | 402 | } |
2302c1ca MAL |
403 | |
404 | qemu_co_mutex_init(&client->send_mutex); | |
9bc9732f | 405 | qemu_co_queue_init(&client->free_sema); |
064097d9 DB |
406 | client->sioc = sioc; |
407 | object_ref(OBJECT(client->sioc)); | |
f95910fe DB |
408 | |
409 | if (!client->ioc) { | |
410 | client->ioc = QIO_CHANNEL(sioc); | |
411 | object_ref(OBJECT(client->ioc)); | |
412 | } | |
2302c1ca MAL |
413 | |
414 | /* Now that we're connected, set the socket to be non-blocking and | |
415 | * kick the reply mechanism. */ | |
064097d9 | 416 | qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); |
ff82911c | 417 | client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client); |
f53a829b | 418 | nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); |
2302c1ca MAL |
419 | |
420 | logout("Established connection with NBD server\n"); | |
421 | return 0; | |
422 | } |