xref: /qemu/blockdev-nbd.c (revision 54fde4ff)
16dd844dbSPaolo Bonzini /*
26dd844dbSPaolo Bonzini  * Serving QEMU block devices via NBD
36dd844dbSPaolo Bonzini  *
46dd844dbSPaolo Bonzini  * Copyright (c) 2012 Red Hat, Inc.
56dd844dbSPaolo Bonzini  *
66dd844dbSPaolo Bonzini  * Author: Paolo Bonzini <pbonzini@redhat.com>
76dd844dbSPaolo Bonzini  *
86dd844dbSPaolo Bonzini  * This work is licensed under the terms of the GNU GPL, version 2 or
96dd844dbSPaolo Bonzini  * later.  See the COPYING file in the top-level directory.
106dd844dbSPaolo Bonzini  */
116dd844dbSPaolo Bonzini 
12d38ea87aSPeter Maydell #include "qemu/osdep.h"
139c17d615SPaolo Bonzini #include "sysemu/blockdev.h"
14e140177dSMax Reitz #include "sysemu/block-backend.h"
150d09e41aSPaolo Bonzini #include "hw/block/block.h"
16e688df6bSMarkus Armbruster #include "qapi/error.h"
178675cbd6SEric Blake #include "qapi/clone-visitor.h"
188675cbd6SEric Blake #include "qapi/qapi-visit-block-export.h"
195daa6bfdSKevin Wolf #include "qapi/qapi-commands-block-export.h"
20737e150eSPaolo Bonzini #include "block/nbd.h"
21ae398278SDaniel P. Berrange #include "io/channel-socket.h"
22862172f4SDaniel P. Berrange #include "io/net-listener.h"
236dd844dbSPaolo Bonzini 
24ddffee39SDaniel P. Berrange typedef struct NBDServerData {
25862172f4SDaniel P. Berrange     QIONetListener *listener;
26ddffee39SDaniel P. Berrange     QCryptoTLSCreds *tlscreds;
2700019455SDaniel P. Berrange     char *tlsauthz;
281c8222b0SKevin Wolf     uint32_t max_connections;
291c8222b0SKevin Wolf     uint32_t connections;
30ddffee39SDaniel P. Berrange } NBDServerData;
31ddffee39SDaniel P. Berrange 
32ddffee39SDaniel P. Berrange static NBDServerData *nbd_server;
33a5fced40SEric Blake static int qemu_nbd_connections = -1; /* Non-negative if this is qemu-nbd */
34ddffee39SDaniel P. Berrange 
351c8222b0SKevin Wolf static void nbd_update_server_watch(NBDServerData *s);
361c8222b0SKevin Wolf 
nbd_server_is_qemu_nbd(int max_connections)37a5fced40SEric Blake void nbd_server_is_qemu_nbd(int max_connections)
3800917172SKevin Wolf {
39a5fced40SEric Blake     qemu_nbd_connections = max_connections;
4000917172SKevin Wolf }
4100917172SKevin Wolf 
nbd_server_is_running(void)425b1cb497SKevin Wolf bool nbd_server_is_running(void)
435b1cb497SKevin Wolf {
44a5fced40SEric Blake     return nbd_server || qemu_nbd_connections >= 0;
455b1cb497SKevin Wolf }
465b1cb497SKevin Wolf 
nbd_server_max_connections(void)4758a6fdccSEric Blake int nbd_server_max_connections(void)
4858a6fdccSEric Blake {
4958a6fdccSEric Blake     return nbd_server ? nbd_server->max_connections : qemu_nbd_connections;
5058a6fdccSEric Blake }
5158a6fdccSEric Blake 
nbd_blockdev_client_closed(NBDClient * client,bool ignored)520c9390d9SEric Blake static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
530c9390d9SEric Blake {
540c9390d9SEric Blake     nbd_client_put(client);
551c8222b0SKevin Wolf     assert(nbd_server->connections > 0);
561c8222b0SKevin Wolf     nbd_server->connections--;
571c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
580c9390d9SEric Blake }
596dd844dbSPaolo Bonzini 
nbd_accept(QIONetListener * listener,QIOChannelSocket * cioc,gpointer opaque)60862172f4SDaniel P. Berrange static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
61ae398278SDaniel P. Berrange                        gpointer opaque)
626dd844dbSPaolo Bonzini {
631c8222b0SKevin Wolf     nbd_server->connections++;
641c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
651c8222b0SKevin Wolf 
660d73f725SDaniel P. Berrange     qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
6700019455SDaniel P. Berrange     nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz,
680c9390d9SEric Blake                    nbd_blockdev_client_closed);
696dd844dbSPaolo Bonzini }
706dd844dbSPaolo Bonzini 
nbd_update_server_watch(NBDServerData * s)711c8222b0SKevin Wolf static void nbd_update_server_watch(NBDServerData *s)
721c8222b0SKevin Wolf {
731c8222b0SKevin Wolf     if (!s->max_connections || s->connections < s->max_connections) {
741c8222b0SKevin Wolf         qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL);
751c8222b0SKevin Wolf     } else {
761c8222b0SKevin Wolf         qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
771c8222b0SKevin Wolf     }
781c8222b0SKevin Wolf }
79ddffee39SDaniel P. Berrange 
nbd_server_free(NBDServerData * server)80ddffee39SDaniel P. Berrange static void nbd_server_free(NBDServerData *server)
816dd844dbSPaolo Bonzini {
82ddffee39SDaniel P. Berrange     if (!server) {
83ddffee39SDaniel P. Berrange         return;
84ddffee39SDaniel P. Berrange     }
85ddffee39SDaniel P. Berrange 
86862172f4SDaniel P. Berrange     qio_net_listener_disconnect(server->listener);
87862172f4SDaniel P. Berrange     object_unref(OBJECT(server->listener));
88ddffee39SDaniel P. Berrange     if (server->tlscreds) {
89ddffee39SDaniel P. Berrange         object_unref(OBJECT(server->tlscreds));
90ddffee39SDaniel P. Berrange     }
9100019455SDaniel P. Berrange     g_free(server->tlsauthz);
92ddffee39SDaniel P. Berrange 
93ddffee39SDaniel P. Berrange     g_free(server);
94ddffee39SDaniel P. Berrange }
95ddffee39SDaniel P. Berrange 
nbd_get_tls_creds(const char * id,Error ** errp)96ddffee39SDaniel P. Berrange static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
97ddffee39SDaniel P. Berrange {
98ddffee39SDaniel P. Berrange     Object *obj;
99ddffee39SDaniel P. Berrange     QCryptoTLSCreds *creds;
100ddffee39SDaniel P. Berrange 
101ddffee39SDaniel P. Berrange     obj = object_resolve_path_component(
102ddffee39SDaniel P. Berrange         object_get_objects_root(), id);
103ddffee39SDaniel P. Berrange     if (!obj) {
104ddffee39SDaniel P. Berrange         error_setg(errp, "No TLS credentials with id '%s'",
105ddffee39SDaniel P. Berrange                    id);
106ddffee39SDaniel P. Berrange         return NULL;
107ddffee39SDaniel P. Berrange     }
108ddffee39SDaniel P. Berrange     creds = (QCryptoTLSCreds *)
109ddffee39SDaniel P. Berrange         object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
110ddffee39SDaniel P. Berrange     if (!creds) {
111ddffee39SDaniel P. Berrange         error_setg(errp, "Object with id '%s' is not TLS credentials",
112ddffee39SDaniel P. Berrange                    id);
113ddffee39SDaniel P. Berrange         return NULL;
114ddffee39SDaniel P. Berrange     }
115ddffee39SDaniel P. Berrange 
1167b3b6168SPhilippe Mathieu-Daudé     if (!qcrypto_tls_creds_check_endpoint(creds,
1177b3b6168SPhilippe Mathieu-Daudé                                           QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
1187b3b6168SPhilippe Mathieu-Daudé                                           errp)) {
119ddffee39SDaniel P. Berrange         return NULL;
120ddffee39SDaniel P. Berrange     }
121ddffee39SDaniel P. Berrange     object_ref(obj);
122ddffee39SDaniel P. Berrange     return creds;
123ddffee39SDaniel P. Berrange }
124ddffee39SDaniel P. Berrange 
125ddffee39SDaniel P. Berrange 
nbd_server_start(SocketAddress * addr,const char * tls_creds,const char * tls_authz,uint32_t max_connections,Error ** errp)126bd269ebcSMarkus Armbruster void nbd_server_start(SocketAddress *addr, const char *tls_creds,
1271c8222b0SKevin Wolf                       const char *tls_authz, uint32_t max_connections,
1281c8222b0SKevin Wolf                       Error **errp)
129ddffee39SDaniel P. Berrange {
130ddffee39SDaniel P. Berrange     if (nbd_server) {
1316dd844dbSPaolo Bonzini         error_setg(errp, "NBD server already running");
1326dd844dbSPaolo Bonzini         return;
1336dd844dbSPaolo Bonzini     }
1346dd844dbSPaolo Bonzini 
135ddffee39SDaniel P. Berrange     nbd_server = g_new0(NBDServerData, 1);
1361c8222b0SKevin Wolf     nbd_server->max_connections = max_connections;
137862172f4SDaniel P. Berrange     nbd_server->listener = qio_net_listener_new();
138862172f4SDaniel P. Berrange 
139862172f4SDaniel P. Berrange     qio_net_listener_set_name(nbd_server->listener,
1400d73f725SDaniel P. Berrange                               "nbd-listener");
141862172f4SDaniel P. Berrange 
142582d4210SEric Blake     /*
143582d4210SEric Blake      * Because this server is persistent, a backlog of SOMAXCONN is
144582d4210SEric Blake      * better than trying to size it to max_connections.
145582d4210SEric Blake      */
146582d4210SEric Blake     if (qio_net_listener_open_sync(nbd_server->listener, addr, SOMAXCONN,
147582d4210SEric Blake                                    errp) < 0) {
148ddffee39SDaniel P. Berrange         goto error;
1496dd844dbSPaolo Bonzini     }
150ae398278SDaniel P. Berrange 
151bd269ebcSMarkus Armbruster     if (tls_creds) {
152ddffee39SDaniel P. Berrange         nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
153ddffee39SDaniel P. Berrange         if (!nbd_server->tlscreds) {
154ddffee39SDaniel P. Berrange             goto error;
155ddffee39SDaniel P. Berrange         }
156ddffee39SDaniel P. Berrange     }
157ddffee39SDaniel P. Berrange 
15800019455SDaniel P. Berrange     nbd_server->tlsauthz = g_strdup(tls_authz);
15900019455SDaniel P. Berrange 
1601c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
161ddffee39SDaniel P. Berrange 
162ddffee39SDaniel P. Berrange     return;
163ddffee39SDaniel P. Berrange 
164ddffee39SDaniel P. Berrange  error:
165ddffee39SDaniel P. Berrange     nbd_server_free(nbd_server);
166ddffee39SDaniel P. Berrange     nbd_server = NULL;
1676dd844dbSPaolo Bonzini }
1686dd844dbSPaolo Bonzini 
nbd_server_start_options(NbdServerOptions * arg,Error ** errp)169eed8b691SKevin Wolf void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
170eed8b691SKevin Wolf {
1711c8222b0SKevin Wolf     nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz,
1721c8222b0SKevin Wolf                      arg->max_connections, errp);
173eed8b691SKevin Wolf }
174eed8b691SKevin Wolf 
qmp_nbd_server_start(SocketAddressLegacy * addr,const char * tls_creds,const char * tls_authz,bool has_max_connections,uint32_t max_connections,Error ** errp)175bd269ebcSMarkus Armbruster void qmp_nbd_server_start(SocketAddressLegacy *addr,
176*54fde4ffSMarkus Armbruster                           const char *tls_creds,
177*54fde4ffSMarkus Armbruster                           const char *tls_authz,
1781c8222b0SKevin Wolf                           bool has_max_connections, uint32_t max_connections,
179bd269ebcSMarkus Armbruster                           Error **errp)
180bd269ebcSMarkus Armbruster {
181bd269ebcSMarkus Armbruster     SocketAddress *addr_flat = socket_address_flatten(addr);
182bd269ebcSMarkus Armbruster 
1831c8222b0SKevin Wolf     nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
184bd269ebcSMarkus Armbruster     qapi_free_SocketAddress(addr_flat);
185bd269ebcSMarkus Armbruster }
186bd269ebcSMarkus Armbruster 
qmp_nbd_server_add(NbdServerAddOptions * arg,Error ** errp)187b6076afcSKevin Wolf void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp)
18856ee8626SKevin Wolf {
1899b562c64SKevin Wolf     BlockExport *export;
1909b562c64SKevin Wolf     BlockDriverState *bs;
1919b562c64SKevin Wolf     BlockBackend *on_eject_blk;
192b6076afcSKevin Wolf     BlockExportOptions *export_opts;
1939b562c64SKevin Wolf 
1949b562c64SKevin Wolf     bs = bdrv_lookup_bs(arg->device, arg->device, errp);
1959b562c64SKevin Wolf     if (!bs) {
1969b562c64SKevin Wolf         return;
1979b562c64SKevin Wolf     }
1989b562c64SKevin Wolf 
199b6076afcSKevin Wolf     /*
200b6076afcSKevin Wolf      * block-export-add would default to the node-name, but we may have to use
201b6076afcSKevin Wolf      * the device name as a default here for compatibility.
202b6076afcSKevin Wolf      */
203*54fde4ffSMarkus Armbruster     if (!arg->name) {
2048675cbd6SEric Blake         arg->name = g_strdup(arg->device);
205b6076afcSKevin Wolf     }
206b6076afcSKevin Wolf 
207b6076afcSKevin Wolf     export_opts = g_new(BlockExportOptions, 1);
208b6076afcSKevin Wolf     *export_opts = (BlockExportOptions) {
20956ee8626SKevin Wolf         .type                   = BLOCK_EXPORT_TYPE_NBD,
210d53be9ceSKevin Wolf         .id                     = g_strdup(arg->name),
211b6076afcSKevin Wolf         .node_name              = g_strdup(bdrv_get_node_name(bs)),
21230dbc81dSKevin Wolf         .has_writable           = arg->has_writable,
21330dbc81dSKevin Wolf         .writable               = arg->writable,
21456ee8626SKevin Wolf     };
215cbad81ceSEric Blake     QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd,
2168675cbd6SEric Blake                        qapi_NbdServerAddOptions_base(arg));
217*54fde4ffSMarkus Armbruster     if (arg->bitmap) {
218e5fb29d5SVladimir Sementsov-Ogievskiy         BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1);
219e5fb29d5SVladimir Sementsov-Ogievskiy 
220e5fb29d5SVladimir Sementsov-Ogievskiy         *el = (BlockDirtyBitmapOrStr) {
221e5fb29d5SVladimir Sementsov-Ogievskiy             .type = QTYPE_QSTRING,
222e5fb29d5SVladimir Sementsov-Ogievskiy             .u.local = g_strdup(arg->bitmap),
223e5fb29d5SVladimir Sementsov-Ogievskiy         };
224cbad81ceSEric Blake         export_opts->u.nbd.has_bitmaps = true;
225e5fb29d5SVladimir Sementsov-Ogievskiy         QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, el);
226cbad81ceSEric Blake     }
2279b562c64SKevin Wolf 
2289b562c64SKevin Wolf     /*
2299b562c64SKevin Wolf      * nbd-server-add doesn't complain when a read-only device should be
2309b562c64SKevin Wolf      * exported as writable, but simply downgrades it. This is an error with
2319b562c64SKevin Wolf      * block-export-add.
2329b562c64SKevin Wolf      */
2339b562c64SKevin Wolf     if (bdrv_is_read_only(bs)) {
23430dbc81dSKevin Wolf         export_opts->has_writable = true;
23530dbc81dSKevin Wolf         export_opts->writable = false;
2369b562c64SKevin Wolf     }
2379b562c64SKevin Wolf 
238b6076afcSKevin Wolf     export = blk_exp_add(export_opts, errp);
2399b562c64SKevin Wolf     if (!export) {
240b6076afcSKevin Wolf         goto fail;
2419b562c64SKevin Wolf     }
2429b562c64SKevin Wolf 
2439b562c64SKevin Wolf     /*
2449b562c64SKevin Wolf      * nbd-server-add removes the export when the named BlockBackend used for
2459b562c64SKevin Wolf      * @device goes away.
2469b562c64SKevin Wolf      */
2479b562c64SKevin Wolf     on_eject_blk = blk_by_name(arg->device);
2489b562c64SKevin Wolf     if (on_eject_blk) {
2499b562c64SKevin Wolf         nbd_export_set_on_eject_blk(export, on_eject_blk);
2509b562c64SKevin Wolf     }
251b6076afcSKevin Wolf 
252b6076afcSKevin Wolf fail:
253b6076afcSKevin Wolf     qapi_free_BlockExportOptions(export_opts);
2546dd844dbSPaolo Bonzini }
2556dd844dbSPaolo Bonzini 
qmp_nbd_server_remove(const char * name,bool has_mode,BlockExportRemoveMode mode,Error ** errp)256a3b0dc75SVladimir Sementsov-Ogievskiy void qmp_nbd_server_remove(const char *name,
2573c3bc462SKevin Wolf                            bool has_mode, BlockExportRemoveMode mode,
258a3b0dc75SVladimir Sementsov-Ogievskiy                            Error **errp)
259a3b0dc75SVladimir Sementsov-Ogievskiy {
2603c3bc462SKevin Wolf     BlockExport *exp;
261a3b0dc75SVladimir Sementsov-Ogievskiy 
2623c3bc462SKevin Wolf     exp = blk_exp_find(name);
2633c3bc462SKevin Wolf     if (exp && exp->drv->type != BLOCK_EXPORT_TYPE_NBD) {
2643c3bc462SKevin Wolf         error_setg(errp, "Block export '%s' is not an NBD export", name);
265a3b0dc75SVladimir Sementsov-Ogievskiy         return;
266a3b0dc75SVladimir Sementsov-Ogievskiy     }
267a3b0dc75SVladimir Sementsov-Ogievskiy 
2683c3bc462SKevin Wolf     qmp_block_export_del(name, has_mode, mode, errp);
269a3b0dc75SVladimir Sementsov-Ogievskiy }
270a3b0dc75SVladimir Sementsov-Ogievskiy 
qmp_nbd_server_stop(Error ** errp)2716dd844dbSPaolo Bonzini void qmp_nbd_server_stop(Error **errp)
2726dd844dbSPaolo Bonzini {
2737801c3a7SEric Blake     if (!nbd_server) {
2747801c3a7SEric Blake         error_setg(errp, "NBD server not running");
2757801c3a7SEric Blake         return;
2767801c3a7SEric Blake     }
2777801c3a7SEric Blake 
278bc4ee65bSKevin Wolf     blk_exp_close_all_type(BLOCK_EXPORT_TYPE_NBD);
2796dd844dbSPaolo Bonzini 
280ddffee39SDaniel P. Berrange     nbd_server_free(nbd_server);
281ddffee39SDaniel P. Berrange     nbd_server = NULL;
282fc6467eaSPaolo Bonzini }
283