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 { 50019d6b8fSAnthony Liguori int sock; 51b90fb4b8SPaolo Bonzini uint32_t nbdflags; 52019d6b8fSAnthony Liguori off_t size; 53019d6b8fSAnthony Liguori size_t blocksize; 5433897dc7SNick Thomas char *export_name; /* An NBD server may export several devices */ 5533897dc7SNick Thomas 56ae255e52SPaolo Bonzini CoMutex mutex; 57ae255e52SPaolo Bonzini Coroutine *coroutine; 58ae255e52SPaolo Bonzini 59ae255e52SPaolo Bonzini struct nbd_reply reply; 60ae255e52SPaolo Bonzini 6133897dc7SNick Thomas /* If it begins with '/', this is a UNIX domain socket. Otherwise, 6233897dc7SNick Thomas * it's a string of the form <hostname|ip4|\[ip6\]>:port 6333897dc7SNick Thomas */ 6433897dc7SNick Thomas char *host_spec; 65019d6b8fSAnthony Liguori } BDRVNBDState; 66019d6b8fSAnthony Liguori 6733897dc7SNick Thomas static int nbd_config(BDRVNBDState *s, const char *filename, int flags) 68019d6b8fSAnthony Liguori { 691d45f8b5SLaurent Vivier char *file; 7033897dc7SNick Thomas char *export_name; 7133897dc7SNick Thomas const char *host_spec; 72019d6b8fSAnthony Liguori const char *unixpath; 731d45f8b5SLaurent Vivier int err = -EINVAL; 74019d6b8fSAnthony Liguori 757267c094SAnthony Liguori file = g_strdup(filename); 761d45f8b5SLaurent Vivier 7733897dc7SNick Thomas export_name = strstr(file, EN_OPTSTR); 7833897dc7SNick Thomas if (export_name) { 7933897dc7SNick Thomas if (export_name[strlen(EN_OPTSTR)] == 0) { 801d45f8b5SLaurent Vivier goto out; 811d45f8b5SLaurent Vivier } 8233897dc7SNick Thomas export_name[0] = 0; /* truncate 'file' */ 8333897dc7SNick Thomas export_name += strlen(EN_OPTSTR); 847267c094SAnthony Liguori s->export_name = g_strdup(export_name); 851d45f8b5SLaurent Vivier } 861d45f8b5SLaurent Vivier 8733897dc7SNick Thomas /* extract the host_spec - fail if it's not nbd:... */ 8833897dc7SNick Thomas if (!strstart(file, "nbd:", &host_spec)) { 891d45f8b5SLaurent Vivier goto out; 901d45f8b5SLaurent Vivier } 91019d6b8fSAnthony Liguori 9233897dc7SNick Thomas /* are we a UNIX or TCP socket? */ 9333897dc7SNick Thomas if (strstart(host_spec, "unix:", &unixpath)) { 9433897dc7SNick Thomas if (unixpath[0] != '/') { /* We demand an absolute path*/ 951d45f8b5SLaurent Vivier goto out; 961d45f8b5SLaurent Vivier } 977267c094SAnthony Liguori s->host_spec = g_strdup(unixpath); 98019d6b8fSAnthony Liguori } else { 997267c094SAnthony Liguori s->host_spec = g_strdup(host_spec); 1001d45f8b5SLaurent Vivier } 1011d45f8b5SLaurent Vivier 1021d45f8b5SLaurent Vivier err = 0; 103019d6b8fSAnthony Liguori 1041d45f8b5SLaurent Vivier out: 1057267c094SAnthony Liguori g_free(file); 10633897dc7SNick Thomas if (err != 0) { 1077267c094SAnthony Liguori g_free(s->export_name); 1087267c094SAnthony Liguori g_free(s->host_spec); 10933897dc7SNick Thomas } 1101d45f8b5SLaurent Vivier return err; 111019d6b8fSAnthony Liguori } 112019d6b8fSAnthony Liguori 113ae255e52SPaolo Bonzini static void nbd_coroutine_start(BDRVNBDState *s, struct nbd_request *request) 114ae255e52SPaolo Bonzini { 115ae255e52SPaolo Bonzini qemu_co_mutex_lock(&s->mutex); 116ae255e52SPaolo Bonzini s->coroutine = qemu_coroutine_self(); 117ae255e52SPaolo Bonzini request->handle = (uint64_t)(intptr_t)s; 118ae255e52SPaolo Bonzini } 119ae255e52SPaolo Bonzini 120ae255e52SPaolo Bonzini static int nbd_have_request(void *opaque) 121ae255e52SPaolo Bonzini { 122ae255e52SPaolo Bonzini BDRVNBDState *s = opaque; 123ae255e52SPaolo Bonzini 124ae255e52SPaolo Bonzini return !!s->coroutine; 125ae255e52SPaolo Bonzini } 126ae255e52SPaolo Bonzini 127ae255e52SPaolo Bonzini static void nbd_reply_ready(void *opaque) 128ae255e52SPaolo Bonzini { 129ae255e52SPaolo Bonzini BDRVNBDState *s = opaque; 130ae255e52SPaolo Bonzini 131ae255e52SPaolo Bonzini if (s->reply.handle == 0) { 132ae255e52SPaolo Bonzini /* No reply already in flight. Fetch a header. */ 133ae255e52SPaolo Bonzini if (nbd_receive_reply(s->sock, &s->reply) < 0) { 134ae255e52SPaolo Bonzini s->reply.handle = 0; 135ae255e52SPaolo Bonzini } 136ae255e52SPaolo Bonzini } 137ae255e52SPaolo Bonzini 138ae255e52SPaolo Bonzini /* There's no need for a mutex on the receive side, because the 139ae255e52SPaolo Bonzini * handler acts as a synchronization point and ensures that only 140ae255e52SPaolo Bonzini * one coroutine is called until the reply finishes. */ 141ae255e52SPaolo Bonzini if (s->coroutine) { 142ae255e52SPaolo Bonzini qemu_coroutine_enter(s->coroutine, NULL); 143ae255e52SPaolo Bonzini } 144ae255e52SPaolo Bonzini } 145ae255e52SPaolo Bonzini 146ae255e52SPaolo Bonzini static void nbd_restart_write(void *opaque) 147ae255e52SPaolo Bonzini { 148ae255e52SPaolo Bonzini BDRVNBDState *s = opaque; 149ae255e52SPaolo Bonzini qemu_coroutine_enter(s->coroutine, NULL); 150ae255e52SPaolo Bonzini } 151ae255e52SPaolo Bonzini 152ae255e52SPaolo Bonzini static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request, 153ae255e52SPaolo Bonzini struct iovec *iov, int offset) 154ae255e52SPaolo Bonzini { 155ae255e52SPaolo Bonzini int rc, ret; 156ae255e52SPaolo Bonzini 157ae255e52SPaolo Bonzini qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write, 158ae255e52SPaolo Bonzini nbd_have_request, NULL, s); 159ae255e52SPaolo Bonzini rc = nbd_send_request(s->sock, request); 160ae255e52SPaolo Bonzini if (rc != -1 && iov) { 161ae255e52SPaolo Bonzini ret = qemu_co_sendv(s->sock, iov, request->len, offset); 162ae255e52SPaolo Bonzini if (ret != request->len) { 163ae255e52SPaolo Bonzini errno = -EIO; 164ae255e52SPaolo Bonzini rc = -1; 165ae255e52SPaolo Bonzini } 166ae255e52SPaolo Bonzini } 167ae255e52SPaolo Bonzini qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, 168ae255e52SPaolo Bonzini nbd_have_request, NULL, s); 169ae255e52SPaolo Bonzini return rc; 170ae255e52SPaolo Bonzini } 171ae255e52SPaolo Bonzini 172ae255e52SPaolo Bonzini static void nbd_co_receive_reply(BDRVNBDState *s, struct nbd_request *request, 173ae255e52SPaolo Bonzini struct nbd_reply *reply, 174ae255e52SPaolo Bonzini struct iovec *iov, int offset) 175ae255e52SPaolo Bonzini { 176ae255e52SPaolo Bonzini int ret; 177ae255e52SPaolo Bonzini 178ae255e52SPaolo Bonzini /* Wait until we're woken up by the read handler. */ 179ae255e52SPaolo Bonzini qemu_coroutine_yield(); 180ae255e52SPaolo Bonzini *reply = s->reply; 181ae255e52SPaolo Bonzini if (reply->handle != request->handle) { 182ae255e52SPaolo Bonzini reply->error = EIO; 183ae255e52SPaolo Bonzini } else { 184ae255e52SPaolo Bonzini if (iov && reply->error == 0) { 185ae255e52SPaolo Bonzini ret = qemu_co_recvv(s->sock, iov, request->len, offset); 186ae255e52SPaolo Bonzini if (ret != request->len) { 187ae255e52SPaolo Bonzini reply->error = EIO; 188ae255e52SPaolo Bonzini } 189ae255e52SPaolo Bonzini } 190ae255e52SPaolo Bonzini 191ae255e52SPaolo Bonzini /* Tell the read handler to read another header. */ 192ae255e52SPaolo Bonzini s->reply.handle = 0; 193ae255e52SPaolo Bonzini } 194ae255e52SPaolo Bonzini } 195ae255e52SPaolo Bonzini 196ae255e52SPaolo Bonzini static void nbd_coroutine_end(BDRVNBDState *s, struct nbd_request *request) 197ae255e52SPaolo Bonzini { 198ae255e52SPaolo Bonzini s->coroutine = NULL; 199ae255e52SPaolo Bonzini qemu_co_mutex_unlock(&s->mutex); 200ae255e52SPaolo Bonzini } 201ae255e52SPaolo Bonzini 20233897dc7SNick Thomas static int nbd_establish_connection(BlockDriverState *bs) 20333897dc7SNick Thomas { 20433897dc7SNick Thomas BDRVNBDState *s = bs->opaque; 20533897dc7SNick Thomas int sock; 20633897dc7SNick Thomas int ret; 20733897dc7SNick Thomas off_t size; 20833897dc7SNick Thomas size_t blocksize; 20933897dc7SNick Thomas 21033897dc7SNick Thomas if (s->host_spec[0] == '/') { 21133897dc7SNick Thomas sock = unix_socket_outgoing(s->host_spec); 21233897dc7SNick Thomas } else { 21333897dc7SNick Thomas sock = tcp_socket_outgoing_spec(s->host_spec); 21433897dc7SNick Thomas } 21533897dc7SNick Thomas 21633897dc7SNick Thomas /* Failed to establish connection */ 21733897dc7SNick Thomas if (sock == -1) { 21833897dc7SNick Thomas logout("Failed to establish connection to NBD server\n"); 21933897dc7SNick Thomas return -errno; 22033897dc7SNick Thomas } 22133897dc7SNick Thomas 22233897dc7SNick Thomas /* NBD handshake */ 223b90fb4b8SPaolo Bonzini ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size, 22433897dc7SNick Thomas &blocksize); 22533897dc7SNick Thomas if (ret == -1) { 22633897dc7SNick Thomas logout("Failed to negotiate with the NBD server\n"); 22733897dc7SNick Thomas closesocket(sock); 22833897dc7SNick Thomas return -errno; 22933897dc7SNick Thomas } 23033897dc7SNick Thomas 231ae255e52SPaolo Bonzini /* Now that we're connected, set the socket to be non-blocking and 232ae255e52SPaolo Bonzini * kick the reply mechanism. */ 23333897dc7SNick Thomas socket_set_nonblock(sock); 234ae255e52SPaolo Bonzini qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL, 235ae255e52SPaolo Bonzini nbd_have_request, NULL, s); 23633897dc7SNick Thomas 23733897dc7SNick Thomas s->sock = sock; 23833897dc7SNick Thomas s->size = size; 23933897dc7SNick Thomas s->blocksize = blocksize; 24033897dc7SNick Thomas 24133897dc7SNick Thomas logout("Established connection with NBD server\n"); 24233897dc7SNick Thomas return 0; 24333897dc7SNick Thomas } 24433897dc7SNick Thomas 24533897dc7SNick Thomas static void nbd_teardown_connection(BlockDriverState *bs) 24633897dc7SNick Thomas { 24733897dc7SNick Thomas BDRVNBDState *s = bs->opaque; 24833897dc7SNick Thomas struct nbd_request request; 24933897dc7SNick Thomas 25033897dc7SNick Thomas request.type = NBD_CMD_DISC; 25133897dc7SNick Thomas request.from = 0; 25233897dc7SNick Thomas request.len = 0; 25333897dc7SNick Thomas nbd_send_request(s->sock, &request); 25433897dc7SNick Thomas 255ae255e52SPaolo Bonzini qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL, NULL); 25633897dc7SNick Thomas closesocket(s->sock); 25733897dc7SNick Thomas } 25833897dc7SNick Thomas 25933897dc7SNick Thomas static int nbd_open(BlockDriverState *bs, const char* filename, int flags) 26033897dc7SNick Thomas { 26133897dc7SNick Thomas BDRVNBDState *s = bs->opaque; 26233897dc7SNick Thomas int result; 26333897dc7SNick Thomas 264ae255e52SPaolo Bonzini qemu_co_mutex_init(&s->mutex); 265ae255e52SPaolo Bonzini 26633897dc7SNick Thomas /* Pop the config into our state object. Exit if invalid. */ 26733897dc7SNick Thomas result = nbd_config(s, filename, flags); 26833897dc7SNick Thomas if (result != 0) { 26933897dc7SNick Thomas return result; 27033897dc7SNick Thomas } 27133897dc7SNick Thomas 27233897dc7SNick Thomas /* establish TCP connection, return error if it fails 27333897dc7SNick Thomas * TODO: Configurable retry-until-timeout behaviour. 27433897dc7SNick Thomas */ 27533897dc7SNick Thomas result = nbd_establish_connection(bs); 27633897dc7SNick Thomas 27733897dc7SNick Thomas return result; 27833897dc7SNick Thomas } 27933897dc7SNick Thomas 280*d9b09f13SPaolo Bonzini static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num, 281*d9b09f13SPaolo Bonzini int nb_sectors, QEMUIOVector *qiov, 282*d9b09f13SPaolo Bonzini int offset) 283019d6b8fSAnthony Liguori { 284019d6b8fSAnthony Liguori BDRVNBDState *s = bs->opaque; 285019d6b8fSAnthony Liguori struct nbd_request request; 286019d6b8fSAnthony Liguori struct nbd_reply reply; 287019d6b8fSAnthony Liguori 288019d6b8fSAnthony Liguori request.type = NBD_CMD_READ; 2893a93113aSDong Xu Wang request.from = sector_num * 512; 290019d6b8fSAnthony Liguori request.len = nb_sectors * 512; 291019d6b8fSAnthony Liguori 292ae255e52SPaolo Bonzini nbd_coroutine_start(s, &request); 293ae255e52SPaolo Bonzini if (nbd_co_send_request(s, &request, NULL, 0) == -1) { 294ae255e52SPaolo Bonzini reply.error = errno; 295ae255e52SPaolo Bonzini } else { 296*d9b09f13SPaolo Bonzini nbd_co_receive_reply(s, &request, &reply, qiov->iov, offset); 297ae255e52SPaolo Bonzini } 298ae255e52SPaolo Bonzini nbd_coroutine_end(s, &request); 299019d6b8fSAnthony Liguori return -reply.error; 300019d6b8fSAnthony Liguori 301019d6b8fSAnthony Liguori } 302019d6b8fSAnthony Liguori 303*d9b09f13SPaolo Bonzini static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num, 304*d9b09f13SPaolo Bonzini int nb_sectors, QEMUIOVector *qiov, 305*d9b09f13SPaolo Bonzini int offset) 306019d6b8fSAnthony Liguori { 307019d6b8fSAnthony Liguori BDRVNBDState *s = bs->opaque; 308019d6b8fSAnthony Liguori struct nbd_request request; 309019d6b8fSAnthony Liguori struct nbd_reply reply; 310019d6b8fSAnthony Liguori 311019d6b8fSAnthony Liguori request.type = NBD_CMD_WRITE; 3123a93113aSDong Xu Wang request.from = sector_num * 512; 313019d6b8fSAnthony Liguori request.len = nb_sectors * 512; 314019d6b8fSAnthony Liguori 315ae255e52SPaolo Bonzini nbd_coroutine_start(s, &request); 316*d9b09f13SPaolo Bonzini if (nbd_co_send_request(s, &request, qiov->iov, offset) == -1) { 317ae255e52SPaolo Bonzini reply.error = errno; 318ae255e52SPaolo Bonzini } else { 319ae255e52SPaolo Bonzini nbd_co_receive_reply(s, &request, &reply, NULL, 0); 320ae255e52SPaolo Bonzini } 321ae255e52SPaolo Bonzini nbd_coroutine_end(s, &request); 322019d6b8fSAnthony Liguori return -reply.error; 323e183ef75SPaolo Bonzini } 324e183ef75SPaolo Bonzini 325*d9b09f13SPaolo Bonzini /* qemu-nbd has a limit of slightly less than 1M per request. Try to 326*d9b09f13SPaolo Bonzini * remain aligned to 4K. */ 327*d9b09f13SPaolo Bonzini #define NBD_MAX_SECTORS 2040 328*d9b09f13SPaolo Bonzini 329*d9b09f13SPaolo Bonzini static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num, 330*d9b09f13SPaolo Bonzini int nb_sectors, QEMUIOVector *qiov) 331*d9b09f13SPaolo Bonzini { 332*d9b09f13SPaolo Bonzini int offset = 0; 333*d9b09f13SPaolo Bonzini int ret; 334*d9b09f13SPaolo Bonzini while (nb_sectors > NBD_MAX_SECTORS) { 335*d9b09f13SPaolo Bonzini ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset); 336*d9b09f13SPaolo Bonzini if (ret < 0) { 337*d9b09f13SPaolo Bonzini return ret; 338*d9b09f13SPaolo Bonzini } 339*d9b09f13SPaolo Bonzini offset += NBD_MAX_SECTORS * 512; 340*d9b09f13SPaolo Bonzini sector_num += NBD_MAX_SECTORS; 341*d9b09f13SPaolo Bonzini nb_sectors -= NBD_MAX_SECTORS; 342*d9b09f13SPaolo Bonzini } 343*d9b09f13SPaolo Bonzini return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset); 344*d9b09f13SPaolo Bonzini } 345*d9b09f13SPaolo Bonzini 346*d9b09f13SPaolo Bonzini static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num, 347*d9b09f13SPaolo Bonzini int nb_sectors, QEMUIOVector *qiov) 348*d9b09f13SPaolo Bonzini { 349*d9b09f13SPaolo Bonzini int offset = 0; 350*d9b09f13SPaolo Bonzini int ret; 351*d9b09f13SPaolo Bonzini while (nb_sectors > NBD_MAX_SECTORS) { 352*d9b09f13SPaolo Bonzini ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset); 353*d9b09f13SPaolo Bonzini if (ret < 0) { 354*d9b09f13SPaolo Bonzini return ret; 355*d9b09f13SPaolo Bonzini } 356*d9b09f13SPaolo Bonzini offset += NBD_MAX_SECTORS * 512; 357*d9b09f13SPaolo Bonzini sector_num += NBD_MAX_SECTORS; 358*d9b09f13SPaolo Bonzini nb_sectors -= NBD_MAX_SECTORS; 359*d9b09f13SPaolo Bonzini } 360*d9b09f13SPaolo Bonzini return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset); 361*d9b09f13SPaolo Bonzini } 362*d9b09f13SPaolo Bonzini 363019d6b8fSAnthony Liguori static void nbd_close(BlockDriverState *bs) 364019d6b8fSAnthony Liguori { 365d2d979c6SNick Thomas BDRVNBDState *s = bs->opaque; 3667267c094SAnthony Liguori g_free(s->export_name); 3677267c094SAnthony Liguori g_free(s->host_spec); 368d2d979c6SNick Thomas 36933897dc7SNick Thomas nbd_teardown_connection(bs); 370019d6b8fSAnthony Liguori } 371019d6b8fSAnthony Liguori 372019d6b8fSAnthony Liguori static int64_t nbd_getlength(BlockDriverState *bs) 373019d6b8fSAnthony Liguori { 374019d6b8fSAnthony Liguori BDRVNBDState *s = bs->opaque; 375019d6b8fSAnthony Liguori 376019d6b8fSAnthony Liguori return s->size; 377019d6b8fSAnthony Liguori } 378019d6b8fSAnthony Liguori 379019d6b8fSAnthony Liguori static BlockDriver bdrv_nbd = { 380019d6b8fSAnthony Liguori .format_name = "nbd", 381019d6b8fSAnthony Liguori .instance_size = sizeof(BDRVNBDState), 38266f82ceeSKevin Wolf .bdrv_file_open = nbd_open, 383ae255e52SPaolo Bonzini .bdrv_co_readv = nbd_co_readv, 384ae255e52SPaolo Bonzini .bdrv_co_writev = nbd_co_writev, 385019d6b8fSAnthony Liguori .bdrv_close = nbd_close, 386019d6b8fSAnthony Liguori .bdrv_getlength = nbd_getlength, 387019d6b8fSAnthony Liguori .protocol_name = "nbd", 388019d6b8fSAnthony Liguori }; 389019d6b8fSAnthony Liguori 390019d6b8fSAnthony Liguori static void bdrv_nbd_init(void) 391019d6b8fSAnthony Liguori { 392019d6b8fSAnthony Liguori bdrv_register(&bdrv_nbd); 393019d6b8fSAnthony Liguori } 394019d6b8fSAnthony Liguori 395019d6b8fSAnthony Liguori block_init(bdrv_nbd_init); 396