xref: /qemu/blockdev-nbd.c (revision 3e61b839844334689cf46694ff1fd1624d6fae20)
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"
1332cad1ffSPhilippe Mathieu-Daudé #include "system/blockdev.h"
1432cad1ffSPhilippe Mathieu-Daudé #include "system/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 
243e7ef738SEric Blake typedef struct NBDConn {
253e7ef738SEric Blake     QIOChannelSocket *cioc;
263e7ef738SEric Blake     QLIST_ENTRY(NBDConn) next;
273e7ef738SEric Blake } NBDConn;
283e7ef738SEric Blake 
29ddffee39SDaniel P. Berrange typedef struct NBDServerData {
30862172f4SDaniel P. Berrange     QIONetListener *listener;
31ff12e6a5SEric Blake     uint32_t handshake_max_secs;
32ddffee39SDaniel P. Berrange     QCryptoTLSCreds *tlscreds;
3300019455SDaniel P. Berrange     char *tlsauthz;
341c8222b0SKevin Wolf     uint32_t max_connections;
351c8222b0SKevin Wolf     uint32_t connections;
363e7ef738SEric Blake     QLIST_HEAD(, NBDConn) conns;
37ddffee39SDaniel P. Berrange } NBDServerData;
38ddffee39SDaniel P. Berrange 
39ddffee39SDaniel P. Berrange static NBDServerData *nbd_server;
40a5fced40SEric Blake static int qemu_nbd_connections = -1; /* Non-negative if this is qemu-nbd */
41ddffee39SDaniel P. Berrange 
421c8222b0SKevin Wolf static void nbd_update_server_watch(NBDServerData *s);
431c8222b0SKevin Wolf 
nbd_server_is_qemu_nbd(int max_connections)44a5fced40SEric Blake void nbd_server_is_qemu_nbd(int max_connections)
4500917172SKevin Wolf {
46a5fced40SEric Blake     qemu_nbd_connections = max_connections;
4700917172SKevin Wolf }
4800917172SKevin Wolf 
nbd_server_is_running(void)495b1cb497SKevin Wolf bool nbd_server_is_running(void)
505b1cb497SKevin Wolf {
51a5fced40SEric Blake     return nbd_server || qemu_nbd_connections >= 0;
525b1cb497SKevin Wolf }
535b1cb497SKevin Wolf 
nbd_server_max_connections(void)5458a6fdccSEric Blake int nbd_server_max_connections(void)
5558a6fdccSEric Blake {
5658a6fdccSEric Blake     return nbd_server ? nbd_server->max_connections : qemu_nbd_connections;
5758a6fdccSEric Blake }
5858a6fdccSEric Blake 
nbd_blockdev_client_closed(NBDClient * client,bool ignored)590c9390d9SEric Blake static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
600c9390d9SEric Blake {
613e7ef738SEric Blake     NBDConn *conn = nbd_client_owner(client);
623e7ef738SEric Blake 
633e7ef738SEric Blake     assert(qemu_in_main_thread() && nbd_server);
643e7ef738SEric Blake 
653e7ef738SEric Blake     object_unref(OBJECT(conn->cioc));
663e7ef738SEric Blake     QLIST_REMOVE(conn, next);
673e7ef738SEric Blake     g_free(conn);
683e7ef738SEric Blake 
690c9390d9SEric Blake     nbd_client_put(client);
701c8222b0SKevin Wolf     assert(nbd_server->connections > 0);
711c8222b0SKevin Wolf     nbd_server->connections--;
721c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
730c9390d9SEric Blake }
746dd844dbSPaolo Bonzini 
nbd_accept(QIONetListener * listener,QIOChannelSocket * cioc,gpointer opaque)75862172f4SDaniel P. Berrange static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
76ae398278SDaniel P. Berrange                        gpointer opaque)
776dd844dbSPaolo Bonzini {
783e7ef738SEric Blake     NBDConn *conn = g_new0(NBDConn, 1);
793e7ef738SEric Blake 
803e7ef738SEric Blake     assert(qemu_in_main_thread() && nbd_server);
811c8222b0SKevin Wolf     nbd_server->connections++;
823e7ef738SEric Blake     object_ref(OBJECT(cioc));
833e7ef738SEric Blake     conn->cioc = cioc;
843e7ef738SEric Blake     QLIST_INSERT_HEAD(&nbd_server->conns, conn, next);
851c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
861c8222b0SKevin Wolf 
870d73f725SDaniel P. Berrange     qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
88ff12e6a5SEric Blake     nbd_client_new(cioc, nbd_server->handshake_max_secs,
89fb1c2aaaSEric Blake                    nbd_server->tlscreds, nbd_server->tlsauthz,
903e7ef738SEric Blake                    nbd_blockdev_client_closed, conn);
916dd844dbSPaolo Bonzini }
926dd844dbSPaolo Bonzini 
nbd_update_server_watch(NBDServerData * s)931c8222b0SKevin Wolf static void nbd_update_server_watch(NBDServerData *s)
941c8222b0SKevin Wolf {
953874f5f7SEric Blake     if (s->listener) {
961c8222b0SKevin Wolf         if (!s->max_connections || s->connections < s->max_connections) {
973874f5f7SEric Blake             qio_net_listener_set_client_func(s->listener, nbd_accept, NULL,
983874f5f7SEric Blake                                              NULL);
991c8222b0SKevin Wolf         } else {
1001c8222b0SKevin Wolf             qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
1011c8222b0SKevin Wolf         }
1021c8222b0SKevin Wolf     }
1033874f5f7SEric Blake }
104ddffee39SDaniel P. Berrange 
nbd_server_free(NBDServerData * server)105ddffee39SDaniel P. Berrange static void nbd_server_free(NBDServerData *server)
1066dd844dbSPaolo Bonzini {
1073e7ef738SEric Blake     NBDConn *conn, *tmp;
1083e7ef738SEric Blake 
109ddffee39SDaniel P. Berrange     if (!server) {
110ddffee39SDaniel P. Berrange         return;
111ddffee39SDaniel P. Berrange     }
112ddffee39SDaniel P. Berrange 
1133e7ef738SEric Blake     /*
1143e7ef738SEric Blake      * Forcefully close the listener socket, and any clients that have
1153e7ef738SEric Blake      * not yet disconnected on their own.
1163e7ef738SEric Blake      */
117862172f4SDaniel P. Berrange     qio_net_listener_disconnect(server->listener);
118862172f4SDaniel P. Berrange     object_unref(OBJECT(server->listener));
1193874f5f7SEric Blake     server->listener = NULL;
1203e7ef738SEric Blake     QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) {
1213e7ef738SEric Blake         qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH,
1223e7ef738SEric Blake                              NULL);
1233e7ef738SEric Blake     }
1243e7ef738SEric Blake 
1253e7ef738SEric Blake     AIO_WAIT_WHILE_UNLOCKED(NULL, server->connections > 0);
1263e7ef738SEric Blake 
127ddffee39SDaniel P. Berrange     if (server->tlscreds) {
128ddffee39SDaniel P. Berrange         object_unref(OBJECT(server->tlscreds));
129ddffee39SDaniel P. Berrange     }
13000019455SDaniel P. Berrange     g_free(server->tlsauthz);
131ddffee39SDaniel P. Berrange 
132ddffee39SDaniel P. Berrange     g_free(server);
133ddffee39SDaniel P. Berrange }
134ddffee39SDaniel P. Berrange 
nbd_get_tls_creds(const char * id,Error ** errp)135ddffee39SDaniel P. Berrange static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
136ddffee39SDaniel P. Berrange {
137ddffee39SDaniel P. Berrange     Object *obj;
138ddffee39SDaniel P. Berrange     QCryptoTLSCreds *creds;
139ddffee39SDaniel P. Berrange 
140ddffee39SDaniel P. Berrange     obj = object_resolve_path_component(
141ddffee39SDaniel P. Berrange         object_get_objects_root(), id);
142ddffee39SDaniel P. Berrange     if (!obj) {
143ddffee39SDaniel P. Berrange         error_setg(errp, "No TLS credentials with id '%s'",
144ddffee39SDaniel P. Berrange                    id);
145ddffee39SDaniel P. Berrange         return NULL;
146ddffee39SDaniel P. Berrange     }
147ddffee39SDaniel P. Berrange     creds = (QCryptoTLSCreds *)
148ddffee39SDaniel P. Berrange         object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
149ddffee39SDaniel P. Berrange     if (!creds) {
150ddffee39SDaniel P. Berrange         error_setg(errp, "Object with id '%s' is not TLS credentials",
151ddffee39SDaniel P. Berrange                    id);
152ddffee39SDaniel P. Berrange         return NULL;
153ddffee39SDaniel P. Berrange     }
154ddffee39SDaniel P. Berrange 
1557b3b6168SPhilippe Mathieu-Daudé     if (!qcrypto_tls_creds_check_endpoint(creds,
1567b3b6168SPhilippe Mathieu-Daudé                                           QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
1577b3b6168SPhilippe Mathieu-Daudé                                           errp)) {
158ddffee39SDaniel P. Berrange         return NULL;
159ddffee39SDaniel P. Berrange     }
160ddffee39SDaniel P. Berrange     object_ref(obj);
161ddffee39SDaniel P. Berrange     return creds;
162ddffee39SDaniel P. Berrange }
163ddffee39SDaniel P. Berrange 
164ddffee39SDaniel P. Berrange 
nbd_server_start(SocketAddress * addr,uint32_t handshake_max_secs,const char * tls_creds,const char * tls_authz,uint32_t max_connections,Error ** errp)165ff12e6a5SEric Blake void nbd_server_start(SocketAddress *addr, uint32_t handshake_max_secs,
166ff12e6a5SEric Blake                       const char *tls_creds, const char *tls_authz,
167ff12e6a5SEric Blake                       uint32_t max_connections, Error **errp)
168ddffee39SDaniel P. Berrange {
169ddffee39SDaniel P. Berrange     if (nbd_server) {
1706dd844dbSPaolo Bonzini         error_setg(errp, "NBD server already running");
1716dd844dbSPaolo Bonzini         return;
1726dd844dbSPaolo Bonzini     }
1736dd844dbSPaolo Bonzini 
174ddffee39SDaniel P. Berrange     nbd_server = g_new0(NBDServerData, 1);
1751c8222b0SKevin Wolf     nbd_server->max_connections = max_connections;
176ff12e6a5SEric Blake     nbd_server->handshake_max_secs = handshake_max_secs;
177862172f4SDaniel P. Berrange     nbd_server->listener = qio_net_listener_new();
178862172f4SDaniel P. Berrange 
179862172f4SDaniel P. Berrange     qio_net_listener_set_name(nbd_server->listener,
1800d73f725SDaniel P. Berrange                               "nbd-listener");
181862172f4SDaniel P. Berrange 
182582d4210SEric Blake     /*
183582d4210SEric Blake      * Because this server is persistent, a backlog of SOMAXCONN is
184582d4210SEric Blake      * better than trying to size it to max_connections.
185582d4210SEric Blake      */
186582d4210SEric Blake     if (qio_net_listener_open_sync(nbd_server->listener, addr, SOMAXCONN,
187582d4210SEric Blake                                    errp) < 0) {
188ddffee39SDaniel P. Berrange         goto error;
1896dd844dbSPaolo Bonzini     }
190ae398278SDaniel P. Berrange 
191bd269ebcSMarkus Armbruster     if (tls_creds) {
192ddffee39SDaniel P. Berrange         nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
193ddffee39SDaniel P. Berrange         if (!nbd_server->tlscreds) {
194ddffee39SDaniel P. Berrange             goto error;
195ddffee39SDaniel P. Berrange         }
196ddffee39SDaniel P. Berrange     }
197ddffee39SDaniel P. Berrange 
19800019455SDaniel P. Berrange     nbd_server->tlsauthz = g_strdup(tls_authz);
19900019455SDaniel P. Berrange 
2001c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
201ddffee39SDaniel P. Berrange 
202ddffee39SDaniel P. Berrange     return;
203ddffee39SDaniel P. Berrange 
204ddffee39SDaniel P. Berrange  error:
205ddffee39SDaniel P. Berrange     nbd_server_free(nbd_server);
206ddffee39SDaniel P. Berrange     nbd_server = NULL;
2076dd844dbSPaolo Bonzini }
2086dd844dbSPaolo Bonzini 
nbd_server_start_options(NbdServerOptions * arg,Error ** errp)209eed8b691SKevin Wolf void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
210eed8b691SKevin Wolf {
211c8a76dbdSEric Blake     if (!arg->has_max_connections) {
212c8a76dbdSEric Blake         arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
213c8a76dbdSEric Blake     }
214ff12e6a5SEric Blake     if (!arg->has_handshake_max_seconds) {
215ff12e6a5SEric Blake         arg->handshake_max_seconds = NBD_DEFAULT_HANDSHAKE_MAX_SECS;
216ff12e6a5SEric Blake     }
217c8a76dbdSEric Blake 
218ff12e6a5SEric Blake     nbd_server_start(arg->addr, arg->handshake_max_seconds, arg->tls_creds,
219ff12e6a5SEric Blake                      arg->tls_authz, arg->max_connections, errp);
220eed8b691SKevin Wolf }
221eed8b691SKevin Wolf 
qmp_nbd_server_start(bool has_handshake_max_secs,uint32_t handshake_max_secs,const char * tls_creds,const char * tls_authz,bool has_max_connections,uint32_t max_connections,SocketAddressLegacy * addr,Error ** errp)222*57f3962bSVladimir Sementsov-Ogievskiy void qmp_nbd_server_start(bool has_handshake_max_secs,
223ff12e6a5SEric Blake                           uint32_t handshake_max_secs,
22454fde4ffSMarkus Armbruster                           const char *tls_creds,
22554fde4ffSMarkus Armbruster                           const char *tls_authz,
2261c8222b0SKevin Wolf                           bool has_max_connections, uint32_t max_connections,
227*57f3962bSVladimir Sementsov-Ogievskiy                           SocketAddressLegacy *addr,
228bd269ebcSMarkus Armbruster                           Error **errp)
229bd269ebcSMarkus Armbruster {
230bd269ebcSMarkus Armbruster     SocketAddress *addr_flat = socket_address_flatten(addr);
231bd269ebcSMarkus Armbruster 
232c8a76dbdSEric Blake     if (!has_max_connections) {
233c8a76dbdSEric Blake         max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
234c8a76dbdSEric Blake     }
235ff12e6a5SEric Blake     if (!has_handshake_max_secs) {
236ff12e6a5SEric Blake         handshake_max_secs = NBD_DEFAULT_HANDSHAKE_MAX_SECS;
237ff12e6a5SEric Blake     }
238c8a76dbdSEric Blake 
239ff12e6a5SEric Blake     nbd_server_start(addr_flat, handshake_max_secs, tls_creds, tls_authz,
240ff12e6a5SEric Blake                      max_connections, errp);
241bd269ebcSMarkus Armbruster     qapi_free_SocketAddress(addr_flat);
242bd269ebcSMarkus Armbruster }
243bd269ebcSMarkus Armbruster 
qmp_nbd_server_add(NbdServerAddOptions * arg,Error ** errp)244b6076afcSKevin Wolf void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp)
24556ee8626SKevin Wolf {
2469b562c64SKevin Wolf     BlockExport *export;
2479b562c64SKevin Wolf     BlockDriverState *bs;
2489b562c64SKevin Wolf     BlockBackend *on_eject_blk;
249b6076afcSKevin Wolf     BlockExportOptions *export_opts;
2509b562c64SKevin Wolf 
2519b562c64SKevin Wolf     bs = bdrv_lookup_bs(arg->device, arg->device, errp);
2529b562c64SKevin Wolf     if (!bs) {
2539b562c64SKevin Wolf         return;
2549b562c64SKevin Wolf     }
2559b562c64SKevin Wolf 
256b6076afcSKevin Wolf     /*
257b6076afcSKevin Wolf      * block-export-add would default to the node-name, but we may have to use
258b6076afcSKevin Wolf      * the device name as a default here for compatibility.
259b6076afcSKevin Wolf      */
26054fde4ffSMarkus Armbruster     if (!arg->name) {
2618675cbd6SEric Blake         arg->name = g_strdup(arg->device);
262b6076afcSKevin Wolf     }
263b6076afcSKevin Wolf 
264b6076afcSKevin Wolf     export_opts = g_new(BlockExportOptions, 1);
265b6076afcSKevin Wolf     *export_opts = (BlockExportOptions) {
26656ee8626SKevin Wolf         .type                   = BLOCK_EXPORT_TYPE_NBD,
267d53be9ceSKevin Wolf         .id                     = g_strdup(arg->name),
268b6076afcSKevin Wolf         .node_name              = g_strdup(bdrv_get_node_name(bs)),
26930dbc81dSKevin Wolf         .has_writable           = arg->has_writable,
27030dbc81dSKevin Wolf         .writable               = arg->writable,
27156ee8626SKevin Wolf     };
272cbad81ceSEric Blake     QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd,
2738675cbd6SEric Blake                        qapi_NbdServerAddOptions_base(arg));
27454fde4ffSMarkus Armbruster     if (arg->bitmap) {
275e5fb29d5SVladimir Sementsov-Ogievskiy         BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1);
276e5fb29d5SVladimir Sementsov-Ogievskiy 
277e5fb29d5SVladimir Sementsov-Ogievskiy         *el = (BlockDirtyBitmapOrStr) {
278e5fb29d5SVladimir Sementsov-Ogievskiy             .type = QTYPE_QSTRING,
279e5fb29d5SVladimir Sementsov-Ogievskiy             .u.local = g_strdup(arg->bitmap),
280e5fb29d5SVladimir Sementsov-Ogievskiy         };
281cbad81ceSEric Blake         export_opts->u.nbd.has_bitmaps = true;
282e5fb29d5SVladimir Sementsov-Ogievskiy         QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, el);
283cbad81ceSEric Blake     }
2849b562c64SKevin Wolf 
2859b562c64SKevin Wolf     /*
2869b562c64SKevin Wolf      * nbd-server-add doesn't complain when a read-only device should be
2879b562c64SKevin Wolf      * exported as writable, but simply downgrades it. This is an error with
2889b562c64SKevin Wolf      * block-export-add.
2899b562c64SKevin Wolf      */
2909b562c64SKevin Wolf     if (bdrv_is_read_only(bs)) {
29130dbc81dSKevin Wolf         export_opts->has_writable = true;
29230dbc81dSKevin Wolf         export_opts->writable = false;
2939b562c64SKevin Wolf     }
2949b562c64SKevin Wolf 
295b6076afcSKevin Wolf     export = blk_exp_add(export_opts, errp);
2969b562c64SKevin Wolf     if (!export) {
297b6076afcSKevin Wolf         goto fail;
2989b562c64SKevin Wolf     }
2999b562c64SKevin Wolf 
3009b562c64SKevin Wolf     /*
3019b562c64SKevin Wolf      * nbd-server-add removes the export when the named BlockBackend used for
3029b562c64SKevin Wolf      * @device goes away.
3039b562c64SKevin Wolf      */
3049b562c64SKevin Wolf     on_eject_blk = blk_by_name(arg->device);
3059b562c64SKevin Wolf     if (on_eject_blk) {
3069b562c64SKevin Wolf         nbd_export_set_on_eject_blk(export, on_eject_blk);
3079b562c64SKevin Wolf     }
308b6076afcSKevin Wolf 
309b6076afcSKevin Wolf fail:
310b6076afcSKevin Wolf     qapi_free_BlockExportOptions(export_opts);
3116dd844dbSPaolo Bonzini }
3126dd844dbSPaolo Bonzini 
qmp_nbd_server_remove(const char * name,bool has_mode,BlockExportRemoveMode mode,Error ** errp)313a3b0dc75SVladimir Sementsov-Ogievskiy void qmp_nbd_server_remove(const char *name,
3143c3bc462SKevin Wolf                            bool has_mode, BlockExportRemoveMode mode,
315a3b0dc75SVladimir Sementsov-Ogievskiy                            Error **errp)
316a3b0dc75SVladimir Sementsov-Ogievskiy {
3173c3bc462SKevin Wolf     BlockExport *exp;
318a3b0dc75SVladimir Sementsov-Ogievskiy 
3193c3bc462SKevin Wolf     exp = blk_exp_find(name);
3203c3bc462SKevin Wolf     if (exp && exp->drv->type != BLOCK_EXPORT_TYPE_NBD) {
3213c3bc462SKevin Wolf         error_setg(errp, "Block export '%s' is not an NBD export", name);
322a3b0dc75SVladimir Sementsov-Ogievskiy         return;
323a3b0dc75SVladimir Sementsov-Ogievskiy     }
324a3b0dc75SVladimir Sementsov-Ogievskiy 
3253c3bc462SKevin Wolf     qmp_block_export_del(name, has_mode, mode, errp);
326a3b0dc75SVladimir Sementsov-Ogievskiy }
327a3b0dc75SVladimir Sementsov-Ogievskiy 
qmp_nbd_server_stop(Error ** errp)3286dd844dbSPaolo Bonzini void qmp_nbd_server_stop(Error **errp)
3296dd844dbSPaolo Bonzini {
3307801c3a7SEric Blake     if (!nbd_server) {
3317801c3a7SEric Blake         error_setg(errp, "NBD server not running");
3327801c3a7SEric Blake         return;
3337801c3a7SEric Blake     }
3347801c3a7SEric Blake 
335bc4ee65bSKevin Wolf     blk_exp_close_all_type(BLOCK_EXPORT_TYPE_NBD);
3366dd844dbSPaolo Bonzini 
337ddffee39SDaniel P. Berrange     nbd_server_free(nbd_server);
338ddffee39SDaniel P. Berrange     nbd_server = NULL;
339fc6467eaSPaolo Bonzini }
340