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