xref: /openbmc/qemu/blockdev-nbd.c (revision afaee42f777bc359db95f692804f7fc7e12c0c02)
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 
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;
31ddffee39SDaniel P. Berrange     QCryptoTLSCreds *tlscreds;
3200019455SDaniel P. Berrange     char *tlsauthz;
331c8222b0SKevin Wolf     uint32_t max_connections;
341c8222b0SKevin Wolf     uint32_t connections;
353e7ef738SEric Blake     QLIST_HEAD(, NBDConn) conns;
36ddffee39SDaniel P. Berrange } NBDServerData;
37ddffee39SDaniel P. Berrange 
38ddffee39SDaniel P. Berrange static NBDServerData *nbd_server;
39a5fced40SEric Blake static int qemu_nbd_connections = -1; /* Non-negative if this is qemu-nbd */
40ddffee39SDaniel P. Berrange 
411c8222b0SKevin Wolf static void nbd_update_server_watch(NBDServerData *s);
421c8222b0SKevin Wolf 
nbd_server_is_qemu_nbd(int max_connections)43a5fced40SEric Blake void nbd_server_is_qemu_nbd(int max_connections)
4400917172SKevin Wolf {
45a5fced40SEric Blake     qemu_nbd_connections = max_connections;
4600917172SKevin Wolf }
4700917172SKevin Wolf 
nbd_server_is_running(void)485b1cb497SKevin Wolf bool nbd_server_is_running(void)
495b1cb497SKevin Wolf {
50a5fced40SEric Blake     return nbd_server || qemu_nbd_connections >= 0;
515b1cb497SKevin Wolf }
525b1cb497SKevin Wolf 
nbd_server_max_connections(void)5358a6fdccSEric Blake int nbd_server_max_connections(void)
5458a6fdccSEric Blake {
5558a6fdccSEric Blake     return nbd_server ? nbd_server->max_connections : qemu_nbd_connections;
5658a6fdccSEric Blake }
5758a6fdccSEric Blake 
nbd_blockdev_client_closed(NBDClient * client,bool ignored)580c9390d9SEric Blake static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
590c9390d9SEric Blake {
603e7ef738SEric Blake     NBDConn *conn = nbd_client_owner(client);
613e7ef738SEric Blake 
623e7ef738SEric Blake     assert(qemu_in_main_thread() && nbd_server);
633e7ef738SEric Blake 
643e7ef738SEric Blake     object_unref(OBJECT(conn->cioc));
653e7ef738SEric Blake     QLIST_REMOVE(conn, next);
663e7ef738SEric Blake     g_free(conn);
673e7ef738SEric Blake 
680c9390d9SEric Blake     nbd_client_put(client);
691c8222b0SKevin Wolf     assert(nbd_server->connections > 0);
701c8222b0SKevin Wolf     nbd_server->connections--;
711c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
720c9390d9SEric Blake }
736dd844dbSPaolo Bonzini 
nbd_accept(QIONetListener * listener,QIOChannelSocket * cioc,gpointer opaque)74862172f4SDaniel P. Berrange static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
75ae398278SDaniel P. Berrange                        gpointer opaque)
766dd844dbSPaolo Bonzini {
773e7ef738SEric Blake     NBDConn *conn = g_new0(NBDConn, 1);
783e7ef738SEric Blake 
793e7ef738SEric Blake     assert(qemu_in_main_thread() && nbd_server);
801c8222b0SKevin Wolf     nbd_server->connections++;
813e7ef738SEric Blake     object_ref(OBJECT(cioc));
823e7ef738SEric Blake     conn->cioc = cioc;
833e7ef738SEric Blake     QLIST_INSERT_HEAD(&nbd_server->conns, conn, next);
841c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
851c8222b0SKevin Wolf 
860d73f725SDaniel P. Berrange     qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
87fb1c2aaaSEric Blake     /* TODO - expose handshake timeout as QMP option */
88fb1c2aaaSEric Blake     nbd_client_new(cioc, NBD_DEFAULT_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 {
95*3874f5f7SEric Blake     if (s->listener) {
961c8222b0SKevin Wolf         if (!s->max_connections || s->connections < s->max_connections) {
97*3874f5f7SEric Blake             qio_net_listener_set_client_func(s->listener, nbd_accept, NULL,
98*3874f5f7SEric Blake                                              NULL);
991c8222b0SKevin Wolf         } else {
1001c8222b0SKevin Wolf             qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
1011c8222b0SKevin Wolf         }
1021c8222b0SKevin Wolf     }
103*3874f5f7SEric 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));
119*3874f5f7SEric 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,const char * tls_creds,const char * tls_authz,uint32_t max_connections,Error ** errp)165bd269ebcSMarkus Armbruster void nbd_server_start(SocketAddress *addr, const char *tls_creds,
1661c8222b0SKevin Wolf                       const char *tls_authz, uint32_t max_connections,
1671c8222b0SKevin Wolf                       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;
176862172f4SDaniel P. Berrange     nbd_server->listener = qio_net_listener_new();
177862172f4SDaniel P. Berrange 
178862172f4SDaniel P. Berrange     qio_net_listener_set_name(nbd_server->listener,
1790d73f725SDaniel P. Berrange                               "nbd-listener");
180862172f4SDaniel P. Berrange 
181582d4210SEric Blake     /*
182582d4210SEric Blake      * Because this server is persistent, a backlog of SOMAXCONN is
183582d4210SEric Blake      * better than trying to size it to max_connections.
184582d4210SEric Blake      */
185582d4210SEric Blake     if (qio_net_listener_open_sync(nbd_server->listener, addr, SOMAXCONN,
186582d4210SEric Blake                                    errp) < 0) {
187ddffee39SDaniel P. Berrange         goto error;
1886dd844dbSPaolo Bonzini     }
189ae398278SDaniel P. Berrange 
190bd269ebcSMarkus Armbruster     if (tls_creds) {
191ddffee39SDaniel P. Berrange         nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
192ddffee39SDaniel P. Berrange         if (!nbd_server->tlscreds) {
193ddffee39SDaniel P. Berrange             goto error;
194ddffee39SDaniel P. Berrange         }
195ddffee39SDaniel P. Berrange     }
196ddffee39SDaniel P. Berrange 
19700019455SDaniel P. Berrange     nbd_server->tlsauthz = g_strdup(tls_authz);
19800019455SDaniel P. Berrange 
1991c8222b0SKevin Wolf     nbd_update_server_watch(nbd_server);
200ddffee39SDaniel P. Berrange 
201ddffee39SDaniel P. Berrange     return;
202ddffee39SDaniel P. Berrange 
203ddffee39SDaniel P. Berrange  error:
204ddffee39SDaniel P. Berrange     nbd_server_free(nbd_server);
205ddffee39SDaniel P. Berrange     nbd_server = NULL;
2066dd844dbSPaolo Bonzini }
2076dd844dbSPaolo Bonzini 
nbd_server_start_options(NbdServerOptions * arg,Error ** errp)208eed8b691SKevin Wolf void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
209eed8b691SKevin Wolf {
210c8a76dbdSEric Blake     if (!arg->has_max_connections) {
211c8a76dbdSEric Blake         arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
212c8a76dbdSEric Blake     }
213c8a76dbdSEric Blake 
2141c8222b0SKevin Wolf     nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz,
2151c8222b0SKevin Wolf                      arg->max_connections, errp);
216eed8b691SKevin Wolf }
217eed8b691SKevin 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)218bd269ebcSMarkus Armbruster void qmp_nbd_server_start(SocketAddressLegacy *addr,
21954fde4ffSMarkus Armbruster                           const char *tls_creds,
22054fde4ffSMarkus Armbruster                           const char *tls_authz,
2211c8222b0SKevin Wolf                           bool has_max_connections, uint32_t max_connections,
222bd269ebcSMarkus Armbruster                           Error **errp)
223bd269ebcSMarkus Armbruster {
224bd269ebcSMarkus Armbruster     SocketAddress *addr_flat = socket_address_flatten(addr);
225bd269ebcSMarkus Armbruster 
226c8a76dbdSEric Blake     if (!has_max_connections) {
227c8a76dbdSEric Blake         max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
228c8a76dbdSEric Blake     }
229c8a76dbdSEric Blake 
2301c8222b0SKevin Wolf     nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
231bd269ebcSMarkus Armbruster     qapi_free_SocketAddress(addr_flat);
232bd269ebcSMarkus Armbruster }
233bd269ebcSMarkus Armbruster 
qmp_nbd_server_add(NbdServerAddOptions * arg,Error ** errp)234b6076afcSKevin Wolf void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp)
23556ee8626SKevin Wolf {
2369b562c64SKevin Wolf     BlockExport *export;
2379b562c64SKevin Wolf     BlockDriverState *bs;
2389b562c64SKevin Wolf     BlockBackend *on_eject_blk;
239b6076afcSKevin Wolf     BlockExportOptions *export_opts;
2409b562c64SKevin Wolf 
2419b562c64SKevin Wolf     bs = bdrv_lookup_bs(arg->device, arg->device, errp);
2429b562c64SKevin Wolf     if (!bs) {
2439b562c64SKevin Wolf         return;
2449b562c64SKevin Wolf     }
2459b562c64SKevin Wolf 
246b6076afcSKevin Wolf     /*
247b6076afcSKevin Wolf      * block-export-add would default to the node-name, but we may have to use
248b6076afcSKevin Wolf      * the device name as a default here for compatibility.
249b6076afcSKevin Wolf      */
25054fde4ffSMarkus Armbruster     if (!arg->name) {
2518675cbd6SEric Blake         arg->name = g_strdup(arg->device);
252b6076afcSKevin Wolf     }
253b6076afcSKevin Wolf 
254b6076afcSKevin Wolf     export_opts = g_new(BlockExportOptions, 1);
255b6076afcSKevin Wolf     *export_opts = (BlockExportOptions) {
25656ee8626SKevin Wolf         .type                   = BLOCK_EXPORT_TYPE_NBD,
257d53be9ceSKevin Wolf         .id                     = g_strdup(arg->name),
258b6076afcSKevin Wolf         .node_name              = g_strdup(bdrv_get_node_name(bs)),
25930dbc81dSKevin Wolf         .has_writable           = arg->has_writable,
26030dbc81dSKevin Wolf         .writable               = arg->writable,
26156ee8626SKevin Wolf     };
262cbad81ceSEric Blake     QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd,
2638675cbd6SEric Blake                        qapi_NbdServerAddOptions_base(arg));
26454fde4ffSMarkus Armbruster     if (arg->bitmap) {
265e5fb29d5SVladimir Sementsov-Ogievskiy         BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1);
266e5fb29d5SVladimir Sementsov-Ogievskiy 
267e5fb29d5SVladimir Sementsov-Ogievskiy         *el = (BlockDirtyBitmapOrStr) {
268e5fb29d5SVladimir Sementsov-Ogievskiy             .type = QTYPE_QSTRING,
269e5fb29d5SVladimir Sementsov-Ogievskiy             .u.local = g_strdup(arg->bitmap),
270e5fb29d5SVladimir Sementsov-Ogievskiy         };
271cbad81ceSEric Blake         export_opts->u.nbd.has_bitmaps = true;
272e5fb29d5SVladimir Sementsov-Ogievskiy         QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, el);
273cbad81ceSEric Blake     }
2749b562c64SKevin Wolf 
2759b562c64SKevin Wolf     /*
2769b562c64SKevin Wolf      * nbd-server-add doesn't complain when a read-only device should be
2779b562c64SKevin Wolf      * exported as writable, but simply downgrades it. This is an error with
2789b562c64SKevin Wolf      * block-export-add.
2799b562c64SKevin Wolf      */
2809b562c64SKevin Wolf     if (bdrv_is_read_only(bs)) {
28130dbc81dSKevin Wolf         export_opts->has_writable = true;
28230dbc81dSKevin Wolf         export_opts->writable = false;
2839b562c64SKevin Wolf     }
2849b562c64SKevin Wolf 
285b6076afcSKevin Wolf     export = blk_exp_add(export_opts, errp);
2869b562c64SKevin Wolf     if (!export) {
287b6076afcSKevin Wolf         goto fail;
2889b562c64SKevin Wolf     }
2899b562c64SKevin Wolf 
2909b562c64SKevin Wolf     /*
2919b562c64SKevin Wolf      * nbd-server-add removes the export when the named BlockBackend used for
2929b562c64SKevin Wolf      * @device goes away.
2939b562c64SKevin Wolf      */
2949b562c64SKevin Wolf     on_eject_blk = blk_by_name(arg->device);
2959b562c64SKevin Wolf     if (on_eject_blk) {
2969b562c64SKevin Wolf         nbd_export_set_on_eject_blk(export, on_eject_blk);
2979b562c64SKevin Wolf     }
298b6076afcSKevin Wolf 
299b6076afcSKevin Wolf fail:
300b6076afcSKevin Wolf     qapi_free_BlockExportOptions(export_opts);
3016dd844dbSPaolo Bonzini }
3026dd844dbSPaolo Bonzini 
qmp_nbd_server_remove(const char * name,bool has_mode,BlockExportRemoveMode mode,Error ** errp)303a3b0dc75SVladimir Sementsov-Ogievskiy void qmp_nbd_server_remove(const char *name,
3043c3bc462SKevin Wolf                            bool has_mode, BlockExportRemoveMode mode,
305a3b0dc75SVladimir Sementsov-Ogievskiy                            Error **errp)
306a3b0dc75SVladimir Sementsov-Ogievskiy {
3073c3bc462SKevin Wolf     BlockExport *exp;
308a3b0dc75SVladimir Sementsov-Ogievskiy 
3093c3bc462SKevin Wolf     exp = blk_exp_find(name);
3103c3bc462SKevin Wolf     if (exp && exp->drv->type != BLOCK_EXPORT_TYPE_NBD) {
3113c3bc462SKevin Wolf         error_setg(errp, "Block export '%s' is not an NBD export", name);
312a3b0dc75SVladimir Sementsov-Ogievskiy         return;
313a3b0dc75SVladimir Sementsov-Ogievskiy     }
314a3b0dc75SVladimir Sementsov-Ogievskiy 
3153c3bc462SKevin Wolf     qmp_block_export_del(name, has_mode, mode, errp);
316a3b0dc75SVladimir Sementsov-Ogievskiy }
317a3b0dc75SVladimir Sementsov-Ogievskiy 
qmp_nbd_server_stop(Error ** errp)3186dd844dbSPaolo Bonzini void qmp_nbd_server_stop(Error **errp)
3196dd844dbSPaolo Bonzini {
3207801c3a7SEric Blake     if (!nbd_server) {
3217801c3a7SEric Blake         error_setg(errp, "NBD server not running");
3227801c3a7SEric Blake         return;
3237801c3a7SEric Blake     }
3247801c3a7SEric Blake 
325bc4ee65bSKevin Wolf     blk_exp_close_all_type(BLOCK_EXPORT_TYPE_NBD);
3266dd844dbSPaolo Bonzini 
327ddffee39SDaniel P. Berrange     nbd_server_free(nbd_server);
328ddffee39SDaniel P. Berrange     nbd_server = NULL;
329fc6467eaSPaolo Bonzini }
330