xref: /openbmc/qemu/block/nbd.c (revision 3a93113a)
1019d6b8fSAnthony Liguori /*
2019d6b8fSAnthony Liguori  * QEMU Block driver for  NBD
3019d6b8fSAnthony Liguori  *
4019d6b8fSAnthony Liguori  * Copyright (C) 2008 Bull S.A.S.
5019d6b8fSAnthony Liguori  *     Author: Laurent Vivier <Laurent.Vivier@bull.net>
6019d6b8fSAnthony Liguori  *
7019d6b8fSAnthony Liguori  * Some parts:
8019d6b8fSAnthony Liguori  *    Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
9019d6b8fSAnthony Liguori  *
10019d6b8fSAnthony Liguori  * Permission is hereby granted, free of charge, to any person obtaining a copy
11019d6b8fSAnthony Liguori  * of this software and associated documentation files (the "Software"), to deal
12019d6b8fSAnthony Liguori  * in the Software without restriction, including without limitation the rights
13019d6b8fSAnthony Liguori  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14019d6b8fSAnthony Liguori  * copies of the Software, and to permit persons to whom the Software is
15019d6b8fSAnthony Liguori  * furnished to do so, subject to the following conditions:
16019d6b8fSAnthony Liguori  *
17019d6b8fSAnthony Liguori  * The above copyright notice and this permission notice shall be included in
18019d6b8fSAnthony Liguori  * all copies or substantial portions of the Software.
19019d6b8fSAnthony Liguori  *
20019d6b8fSAnthony Liguori  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21019d6b8fSAnthony Liguori  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22019d6b8fSAnthony Liguori  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23019d6b8fSAnthony Liguori  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24019d6b8fSAnthony Liguori  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25019d6b8fSAnthony Liguori  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26019d6b8fSAnthony Liguori  * THE SOFTWARE.
27019d6b8fSAnthony Liguori  */
28019d6b8fSAnthony Liguori 
29019d6b8fSAnthony Liguori #include "qemu-common.h"
30019d6b8fSAnthony Liguori #include "nbd.h"
31ab359cd1SMarkus Armbruster #include "block_int.h"
32019d6b8fSAnthony Liguori #include "module.h"
3333897dc7SNick Thomas #include "qemu_socket.h"
34019d6b8fSAnthony Liguori 
35019d6b8fSAnthony Liguori #include <sys/types.h>
36019d6b8fSAnthony Liguori #include <unistd.h>
37019d6b8fSAnthony Liguori 
381d45f8b5SLaurent Vivier #define EN_OPTSTR ":exportname="
391d45f8b5SLaurent Vivier 
4033897dc7SNick Thomas /* #define DEBUG_NBD */
4133897dc7SNick Thomas 
4233897dc7SNick Thomas #if defined(DEBUG_NBD)
4333897dc7SNick Thomas #define logout(fmt, ...) \
4433897dc7SNick Thomas                 fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
4533897dc7SNick Thomas #else
4633897dc7SNick Thomas #define logout(fmt, ...) ((void)0)
4733897dc7SNick Thomas #endif
4833897dc7SNick Thomas 
49019d6b8fSAnthony Liguori typedef struct BDRVNBDState {
50848c66e8SPaolo Bonzini     CoMutex lock;
51019d6b8fSAnthony Liguori     int sock;
52b90fb4b8SPaolo Bonzini     uint32_t nbdflags;
53019d6b8fSAnthony Liguori     off_t size;
54019d6b8fSAnthony Liguori     size_t blocksize;
5533897dc7SNick Thomas     char *export_name; /* An NBD server may export several devices */
5633897dc7SNick Thomas 
5733897dc7SNick Thomas     /* If it begins with  '/', this is a UNIX domain socket. Otherwise,
5833897dc7SNick Thomas      * it's a string of the form <hostname|ip4|\[ip6\]>:port
5933897dc7SNick Thomas      */
6033897dc7SNick Thomas     char *host_spec;
61019d6b8fSAnthony Liguori } BDRVNBDState;
62019d6b8fSAnthony Liguori 
6333897dc7SNick Thomas static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
64019d6b8fSAnthony Liguori {
651d45f8b5SLaurent Vivier     char *file;
6633897dc7SNick Thomas     char *export_name;
6733897dc7SNick Thomas     const char *host_spec;
68019d6b8fSAnthony Liguori     const char *unixpath;
691d45f8b5SLaurent Vivier     int err = -EINVAL;
70019d6b8fSAnthony Liguori 
717267c094SAnthony Liguori     file = g_strdup(filename);
721d45f8b5SLaurent Vivier 
7333897dc7SNick Thomas     export_name = strstr(file, EN_OPTSTR);
7433897dc7SNick Thomas     if (export_name) {
7533897dc7SNick Thomas         if (export_name[strlen(EN_OPTSTR)] == 0) {
761d45f8b5SLaurent Vivier             goto out;
771d45f8b5SLaurent Vivier         }
7833897dc7SNick Thomas         export_name[0] = 0; /* truncate 'file' */
7933897dc7SNick Thomas         export_name += strlen(EN_OPTSTR);
807267c094SAnthony Liguori         s->export_name = g_strdup(export_name);
811d45f8b5SLaurent Vivier     }
821d45f8b5SLaurent Vivier 
8333897dc7SNick Thomas     /* extract the host_spec - fail if it's not nbd:... */
8433897dc7SNick Thomas     if (!strstart(file, "nbd:", &host_spec)) {
851d45f8b5SLaurent Vivier         goto out;
861d45f8b5SLaurent Vivier     }
87019d6b8fSAnthony Liguori 
8833897dc7SNick Thomas     /* are we a UNIX or TCP socket? */
8933897dc7SNick Thomas     if (strstart(host_spec, "unix:", &unixpath)) {
9033897dc7SNick Thomas         if (unixpath[0] != '/') { /* We demand  an absolute path*/
911d45f8b5SLaurent Vivier             goto out;
921d45f8b5SLaurent Vivier         }
937267c094SAnthony Liguori         s->host_spec = g_strdup(unixpath);
94019d6b8fSAnthony Liguori     } else {
957267c094SAnthony Liguori         s->host_spec = g_strdup(host_spec);
961d45f8b5SLaurent Vivier     }
971d45f8b5SLaurent Vivier 
981d45f8b5SLaurent Vivier     err = 0;
99019d6b8fSAnthony Liguori 
1001d45f8b5SLaurent Vivier out:
1017267c094SAnthony Liguori     g_free(file);
10233897dc7SNick Thomas     if (err != 0) {
1037267c094SAnthony Liguori         g_free(s->export_name);
1047267c094SAnthony Liguori         g_free(s->host_spec);
10533897dc7SNick Thomas     }
1061d45f8b5SLaurent Vivier     return err;
107019d6b8fSAnthony Liguori }
108019d6b8fSAnthony Liguori 
10933897dc7SNick Thomas static int nbd_establish_connection(BlockDriverState *bs)
11033897dc7SNick Thomas {
11133897dc7SNick Thomas     BDRVNBDState *s = bs->opaque;
11233897dc7SNick Thomas     int sock;
11333897dc7SNick Thomas     int ret;
11433897dc7SNick Thomas     off_t size;
11533897dc7SNick Thomas     size_t blocksize;
11633897dc7SNick Thomas 
11733897dc7SNick Thomas     if (s->host_spec[0] == '/') {
11833897dc7SNick Thomas         sock = unix_socket_outgoing(s->host_spec);
11933897dc7SNick Thomas     } else {
12033897dc7SNick Thomas         sock = tcp_socket_outgoing_spec(s->host_spec);
12133897dc7SNick Thomas     }
12233897dc7SNick Thomas 
12333897dc7SNick Thomas     /* Failed to establish connection */
12433897dc7SNick Thomas     if (sock == -1) {
12533897dc7SNick Thomas         logout("Failed to establish connection to NBD server\n");
12633897dc7SNick Thomas         return -errno;
12733897dc7SNick Thomas     }
12833897dc7SNick Thomas 
12933897dc7SNick Thomas     /* NBD handshake */
130b90fb4b8SPaolo Bonzini     ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size,
13133897dc7SNick Thomas                                 &blocksize);
13233897dc7SNick Thomas     if (ret == -1) {
13333897dc7SNick Thomas         logout("Failed to negotiate with the NBD server\n");
13433897dc7SNick Thomas         closesocket(sock);
13533897dc7SNick Thomas         return -errno;
13633897dc7SNick Thomas     }
13733897dc7SNick Thomas 
13833897dc7SNick Thomas     /* Now that we're connected, set the socket to be non-blocking */
13933897dc7SNick Thomas     socket_set_nonblock(sock);
14033897dc7SNick Thomas 
14133897dc7SNick Thomas     s->sock = sock;
14233897dc7SNick Thomas     s->size = size;
14333897dc7SNick Thomas     s->blocksize = blocksize;
14433897dc7SNick Thomas 
14533897dc7SNick Thomas     logout("Established connection with NBD server\n");
14633897dc7SNick Thomas     return 0;
14733897dc7SNick Thomas }
14833897dc7SNick Thomas 
14933897dc7SNick Thomas static void nbd_teardown_connection(BlockDriverState *bs)
15033897dc7SNick Thomas {
15133897dc7SNick Thomas     BDRVNBDState *s = bs->opaque;
15233897dc7SNick Thomas     struct nbd_request request;
15333897dc7SNick Thomas 
15433897dc7SNick Thomas     request.type = NBD_CMD_DISC;
15533897dc7SNick Thomas     request.handle = (uint64_t)(intptr_t)bs;
15633897dc7SNick Thomas     request.from = 0;
15733897dc7SNick Thomas     request.len = 0;
15833897dc7SNick Thomas     nbd_send_request(s->sock, &request);
15933897dc7SNick Thomas 
16033897dc7SNick Thomas     closesocket(s->sock);
16133897dc7SNick Thomas }
16233897dc7SNick Thomas 
16333897dc7SNick Thomas static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
16433897dc7SNick Thomas {
16533897dc7SNick Thomas     BDRVNBDState *s = bs->opaque;
16633897dc7SNick Thomas     int result;
16733897dc7SNick Thomas 
16833897dc7SNick Thomas     /* Pop the config into our state object. Exit if invalid. */
16933897dc7SNick Thomas     result = nbd_config(s, filename, flags);
17033897dc7SNick Thomas     if (result != 0) {
17133897dc7SNick Thomas         return result;
17233897dc7SNick Thomas     }
17333897dc7SNick Thomas 
17433897dc7SNick Thomas     /* establish TCP connection, return error if it fails
17533897dc7SNick Thomas      * TODO: Configurable retry-until-timeout behaviour.
17633897dc7SNick Thomas      */
17733897dc7SNick Thomas     result = nbd_establish_connection(bs);
17833897dc7SNick Thomas 
179848c66e8SPaolo Bonzini     qemu_co_mutex_init(&s->lock);
18033897dc7SNick Thomas     return result;
18133897dc7SNick Thomas }
18233897dc7SNick Thomas 
183019d6b8fSAnthony Liguori static int nbd_read(BlockDriverState *bs, int64_t sector_num,
184019d6b8fSAnthony Liguori                     uint8_t *buf, int nb_sectors)
185019d6b8fSAnthony Liguori {
186019d6b8fSAnthony Liguori     BDRVNBDState *s = bs->opaque;
187019d6b8fSAnthony Liguori     struct nbd_request request;
188019d6b8fSAnthony Liguori     struct nbd_reply reply;
189019d6b8fSAnthony Liguori 
190019d6b8fSAnthony Liguori     request.type = NBD_CMD_READ;
191019d6b8fSAnthony Liguori     request.handle = (uint64_t)(intptr_t)bs;
192*3a93113aSDong Xu Wang     request.from = sector_num * 512;
193019d6b8fSAnthony Liguori     request.len = nb_sectors * 512;
194019d6b8fSAnthony Liguori 
195019d6b8fSAnthony Liguori     if (nbd_send_request(s->sock, &request) == -1)
196019d6b8fSAnthony Liguori         return -errno;
197019d6b8fSAnthony Liguori 
198019d6b8fSAnthony Liguori     if (nbd_receive_reply(s->sock, &reply) == -1)
199019d6b8fSAnthony Liguori         return -errno;
200019d6b8fSAnthony Liguori 
201019d6b8fSAnthony Liguori     if (reply.error !=0)
202019d6b8fSAnthony Liguori         return -reply.error;
203019d6b8fSAnthony Liguori 
204019d6b8fSAnthony Liguori     if (reply.handle != request.handle)
205019d6b8fSAnthony Liguori         return -EIO;
206019d6b8fSAnthony Liguori 
207019d6b8fSAnthony Liguori     if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
208019d6b8fSAnthony Liguori         return -EIO;
209019d6b8fSAnthony Liguori 
210019d6b8fSAnthony Liguori     return 0;
211019d6b8fSAnthony Liguori }
212019d6b8fSAnthony Liguori 
213019d6b8fSAnthony Liguori static int nbd_write(BlockDriverState *bs, int64_t sector_num,
214019d6b8fSAnthony Liguori                      const uint8_t *buf, int nb_sectors)
215019d6b8fSAnthony Liguori {
216019d6b8fSAnthony Liguori     BDRVNBDState *s = bs->opaque;
217019d6b8fSAnthony Liguori     struct nbd_request request;
218019d6b8fSAnthony Liguori     struct nbd_reply reply;
219019d6b8fSAnthony Liguori 
220019d6b8fSAnthony Liguori     request.type = NBD_CMD_WRITE;
221019d6b8fSAnthony Liguori     request.handle = (uint64_t)(intptr_t)bs;
222*3a93113aSDong Xu Wang     request.from = sector_num * 512;
223019d6b8fSAnthony Liguori     request.len = nb_sectors * 512;
224019d6b8fSAnthony Liguori 
225019d6b8fSAnthony Liguori     if (nbd_send_request(s->sock, &request) == -1)
226019d6b8fSAnthony Liguori         return -errno;
227019d6b8fSAnthony Liguori 
228019d6b8fSAnthony Liguori     if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
229019d6b8fSAnthony Liguori         return -EIO;
230019d6b8fSAnthony Liguori 
231019d6b8fSAnthony Liguori     if (nbd_receive_reply(s->sock, &reply) == -1)
232019d6b8fSAnthony Liguori         return -errno;
233019d6b8fSAnthony Liguori 
234019d6b8fSAnthony Liguori     if (reply.error !=0)
235019d6b8fSAnthony Liguori         return -reply.error;
236019d6b8fSAnthony Liguori 
237019d6b8fSAnthony Liguori     if (reply.handle != request.handle)
238019d6b8fSAnthony Liguori         return -EIO;
239019d6b8fSAnthony Liguori 
240019d6b8fSAnthony Liguori     return 0;
241019d6b8fSAnthony Liguori }
242019d6b8fSAnthony Liguori 
2432914caa0SPaolo Bonzini static coroutine_fn int nbd_co_read(BlockDriverState *bs, int64_t sector_num,
2442914caa0SPaolo Bonzini                                     uint8_t *buf, int nb_sectors)
2452914caa0SPaolo Bonzini {
2462914caa0SPaolo Bonzini     int ret;
2472914caa0SPaolo Bonzini     BDRVNBDState *s = bs->opaque;
2482914caa0SPaolo Bonzini     qemu_co_mutex_lock(&s->lock);
2492914caa0SPaolo Bonzini     ret = nbd_read(bs, sector_num, buf, nb_sectors);
2502914caa0SPaolo Bonzini     qemu_co_mutex_unlock(&s->lock);
2512914caa0SPaolo Bonzini     return ret;
2522914caa0SPaolo Bonzini }
2532914caa0SPaolo Bonzini 
254e183ef75SPaolo Bonzini static coroutine_fn int nbd_co_write(BlockDriverState *bs, int64_t sector_num,
255e183ef75SPaolo Bonzini                                      const uint8_t *buf, int nb_sectors)
256e183ef75SPaolo Bonzini {
257e183ef75SPaolo Bonzini     int ret;
258e183ef75SPaolo Bonzini     BDRVNBDState *s = bs->opaque;
259e183ef75SPaolo Bonzini     qemu_co_mutex_lock(&s->lock);
260e183ef75SPaolo Bonzini     ret = nbd_write(bs, sector_num, buf, nb_sectors);
261e183ef75SPaolo Bonzini     qemu_co_mutex_unlock(&s->lock);
262e183ef75SPaolo Bonzini     return ret;
263e183ef75SPaolo Bonzini }
264e183ef75SPaolo Bonzini 
265019d6b8fSAnthony Liguori static void nbd_close(BlockDriverState *bs)
266019d6b8fSAnthony Liguori {
267d2d979c6SNick Thomas     BDRVNBDState *s = bs->opaque;
2687267c094SAnthony Liguori     g_free(s->export_name);
2697267c094SAnthony Liguori     g_free(s->host_spec);
270d2d979c6SNick Thomas 
27133897dc7SNick Thomas     nbd_teardown_connection(bs);
272019d6b8fSAnthony Liguori }
273019d6b8fSAnthony Liguori 
274019d6b8fSAnthony Liguori static int64_t nbd_getlength(BlockDriverState *bs)
275019d6b8fSAnthony Liguori {
276019d6b8fSAnthony Liguori     BDRVNBDState *s = bs->opaque;
277019d6b8fSAnthony Liguori 
278019d6b8fSAnthony Liguori     return s->size;
279019d6b8fSAnthony Liguori }
280019d6b8fSAnthony Liguori 
281019d6b8fSAnthony Liguori static BlockDriver bdrv_nbd = {
282019d6b8fSAnthony Liguori     .format_name	= "nbd",
283019d6b8fSAnthony Liguori     .instance_size	= sizeof(BDRVNBDState),
28466f82ceeSKevin Wolf     .bdrv_file_open	= nbd_open,
2852914caa0SPaolo Bonzini     .bdrv_read          = nbd_co_read,
286e183ef75SPaolo Bonzini     .bdrv_write         = nbd_co_write,
287019d6b8fSAnthony Liguori     .bdrv_close		= nbd_close,
288019d6b8fSAnthony Liguori     .bdrv_getlength	= nbd_getlength,
289019d6b8fSAnthony Liguori     .protocol_name	= "nbd",
290019d6b8fSAnthony Liguori };
291019d6b8fSAnthony Liguori 
292019d6b8fSAnthony Liguori static void bdrv_nbd_init(void)
293019d6b8fSAnthony Liguori {
294019d6b8fSAnthony Liguori     bdrv_register(&bdrv_nbd);
295019d6b8fSAnthony Liguori }
296019d6b8fSAnthony Liguori 
297019d6b8fSAnthony Liguori block_init(bdrv_nbd_init);
298