Message ID | 1349103144-6827-9-git-send-email-pbonzini@redhat.com |
---|---|
State | New |
Headers | show |
On 10/01/2012 08:52 AM, Paolo Bonzini wrote: > Adding an NBD server inside QEMU is trivial, since all the logic is > in nbd.c and can be shared easily between qemu-nbd and QEMU itself. > The main difference is that qemu-nbd serves a single unnamed export, > while QEMU serves named exports. > > +# @nbd-server-add: > +# > +# Export a device to QEMU's embedded NBD server. > +# > +# @device: Block device to be exported > +# > +# @writable: Whether clients should be able to write to the device via the > +# NBD connection (default false). #optional #optional should come right after @writable:
On Mon, 1 Oct 2012 16:52:23 +0200 Paolo Bonzini <pbonzini@redhat.com> wrote: > Adding an NBD server inside QEMU is trivial, since all the logic is > in nbd.c and can be shared easily between qemu-nbd and QEMU itself. > The main difference is that qemu-nbd serves a single unnamed export, > while QEMU serves named exports. > > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Acked-by: Luiz Capitulino <lcapitulino@redhat.com> > --- > Makefile.objs | 2 +- > blockdev-nbd.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > qapi-schema.json | 43 ++++++++++++++++++++ > qmp-commands.hx | 16 ++++++++ > 4 file modificati, 179 inserzioni(+). 1 rimozione(-) > create mode 100644 blockdev-nbd.c > > diff --git a/Makefile.objs b/Makefile.objs > index 124f783..40384ff 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -61,7 +61,7 @@ endif > # suppress *all* target specific code in case of system emulation, i.e. a > # single QEMU executable should support all CPUs and machines. > > -common-obj-y = $(block-obj-y) blockdev.o > +common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o > common-obj-y += net.o net/ > common-obj-y += qom/ > common-obj-y += readline.o console.o cursor.o > diff --git a/blockdev-nbd.c b/blockdev-nbd.c > new file mode 100644 > index 0000000..8031813 > --- /dev/null > +++ b/blockdev-nbd.c > @@ -0,0 +1,119 @@ > +/* > + * Serving QEMU block devices via NBD > + * > + * Copyright (c) 2012 Red Hat, Inc. > + * > + * Author: Paolo Bonzini <pbonzini@redhat.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > + * later. See the COPYING file in the top-level directory. > + */ > + > +#include "blockdev.h" > +#include "hw/block-common.h" > +#include "monitor.h" > +#include "qerror.h" > +#include "sysemu.h" > +#include "qmp-commands.h" > +#include "trace.h" > +#include "nbd.h" > +#include "qemu_socket.h" > + > +static int server_fd = -1; > + > +static void nbd_accept(void *opaque) > +{ > + struct sockaddr_in addr; > + socklen_t addr_len = sizeof(addr); > + > + int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); > + if (fd >= 0) { > + nbd_client_new(NULL, fd, nbd_client_put); > + } > +} > + > +void qmp_nbd_server_start(SocketAddress *addr, Error **errp) > +{ > + if (server_fd != -1) { > + error_setg(errp, "NBD server already running"); > + return; > + } > + > + server_fd = socket_listen(addr, errp); > + if (server_fd != -1) { > + qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL); > + } > +} > + > +/* Hook into the BlockDriverState notifiers to close the export when > + * the file is closed. > + */ > +typedef struct NBDCloseNotifier { > + Notifier n; > + NBDExport *exp; > + QTAILQ_ENTRY(NBDCloseNotifier) next; > +} NBDCloseNotifier; > + > +static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers = > + QTAILQ_HEAD_INITIALIZER(close_notifiers); > + > +static void nbd_close_notifier(Notifier *n, void *data) > +{ > + NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n); > + > + notifier_remove(&cn->n); > + QTAILQ_REMOVE(&close_notifiers, cn, next); > + > + nbd_export_close(cn->exp); > + nbd_export_put(cn->exp); > + g_free(cn); > +} > + > +static void nbd_server_put_ref(NBDExport *exp) > +{ > + BlockDriverState *bs = nbd_export_get_blockdev(exp); > + drive_put_ref(drive_get_by_blockdev(bs)); > +} > + > +void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, > + Error **errp) > +{ > + BlockDriverState *bs; > + NBDExport *exp; > + NBDCloseNotifier *n; > + > + if (nbd_export_find(device)) { > + error_setg(errp, "NBD server already exporting device '%s'", device); > + return; > + } > + > + bs = bdrv_find(device); > + if (!bs) { > + error_set(errp, QERR_DEVICE_NOT_FOUND, device); > + return; > + } > + > + exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, > + nbd_server_put_ref); > + > + nbd_export_set_name(exp, device); > + drive_get_ref(drive_get_by_blockdev(bs)); > + > + n = g_malloc0(sizeof(NBDCloseNotifier)); > + n->n.notify = nbd_close_notifier; > + n->exp = exp; > + bdrv_add_close_notifier(bs, &n->n); > + QTAILQ_INSERT_TAIL(&close_notifiers, n, next); > +} > + > +void qmp_nbd_server_stop(Error **errp) > +{ > + while (!QTAILQ_EMPTY(&close_notifiers)) { > + NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers); > + nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp)); > + } > + > + qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL); > + close(server_fd); > + server_fd = -1; > +} > diff --git a/qapi-schema.json b/qapi-schema.json > index b443a99..229421d 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -2697,3 +2697,46 @@ > # Since: 0.14.0 > ## > { 'command': 'screendump', 'data': {'filename': 'str'} } > + > +## > +# @nbd-server-start: > +# > +# Start an NBD server listening on the given host and port. Block > +# devices can then be exported using @nbd-server-add. The NBD > +# server will present them as named exports; for example, another > +# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME". > +# > +# @addr: Address on which to listen. > +# > +# Returns: error if the server is already running. > +# > +# Since: 1.3.0 > +## > +{ 'command': 'nbd-server-start', > + 'data': { 'addr': 'SocketAddress' } } > + > +## > +# @nbd-server-add: > +# > +# Export a device to QEMU's embedded NBD server. > +# > +# @device: Block device to be exported > +# > +# @writable: Whether clients should be able to write to the device via the > +# NBD connection (default false). #optional > +# > +# Returns: error if the device is already marked for export. > +# > +# Since: 1.3.0 > +## > +{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool'} } > + > +## > +# @nbd-server-stop: > +# > +# Stop QEMU's embedded NBD server, and unregister all devices previously > +# added via @nbd-server-add. > +# > +# Since: 1.3.0 > +## > +{ 'command': 'nbd-server-stop' } > diff --git a/qmp-commands.hx b/qmp-commands.hx > index 36e08d9..c394af0 100644 > --- a/qmp-commands.hx > +++ b/qmp-commands.hx > @@ -2503,6 +2503,22 @@ EQMP > }, > > { > + .name = "nbd-server-start", > + .args_type = "addr:q", > + .mhandler.cmd_new = qmp_marshal_input_nbd_server_start, > + }, > + { > + .name = "nbd-server-add", > + .args_type = "device:B,writable:b?", > + .mhandler.cmd_new = qmp_marshal_input_nbd_server_add, > + }, > + { > + .name = "nbd-server-stop", > + .args_type = "", > + .mhandler.cmd_new = qmp_marshal_input_nbd_server_stop, > + }, > + > + { > .name = "change-vnc-password", > .args_type = "password:s", > .mhandler.cmd_new = qmp_marshal_input_change_vnc_password,
On Mon, Oct 01, 2012 at 04:52:23PM +0200, Paolo Bonzini wrote: > Adding an NBD server inside QEMU is trivial, since all the logic is > in nbd.c and can be shared easily between qemu-nbd and QEMU itself. > The main difference is that qemu-nbd serves a single unnamed export, > while QEMU serves named exports. I've started playing around with qemu-nbd and have started looking into named exports, aio, and adding snapshort support. I'm wondering if instead of adding this I should try to base it on your embedded server. That would give me the named exports, aio and monitor/qmp based snapshot creatation. The only thing to add would be a qemu mode where it doesn't run an actual guest. Does anyone have opinions on this?
Il 31/10/2012 12:23, Christoph Hellwig ha scritto: > On Mon, Oct 01, 2012 at 04:52:23PM +0200, Paolo Bonzini wrote: >> Adding an NBD server inside QEMU is trivial, since all the logic is >> in nbd.c and can be shared easily between qemu-nbd and QEMU itself. >> The main difference is that qemu-nbd serves a single unnamed export, >> while QEMU serves named exports. > > I've started playing around with qemu-nbd and have started looking into > named exports, aio, and adding snapshort support. I'm wondering if instead > of adding this I should try to base it on your embedded server. qemu-nbd does support AIO in the latest versions. There's also --cache=MODE and --aio=MODE command-line options. In fact, a large part of the code is shared between qemu-nbd and the embedded server. The only difference is that qemu-nbd uses unnamed exports, while qemu names them. qemu-nbd also has options to show only one partition, which you probably do not need. But if you need a QMP interface, adding it to qemu-nbd would really be a bad idea. :) > That would give me the named exports, aio and monitor/qmp based > snapshot creation. > > The only thing to add would be a qemu mode where it doesn't run an > actual guest. You can use qtest mode to get very close to this (even if you send stop/cont by mistake on the monitor, no code will actually run): qemu-system-x86_64 -chardev file,id=null,path=/dev/null -qtest null -machine accel=qtest -m 1 -nodefaults -nographic but having a separate do-nothing target would probably be nicer... though Anthony may have different opinions. Paolo > Does anyone have opinions on this?
On Wed, Oct 31, 2012 at 01:46:22PM +0100, Paolo Bonzini wrote: > qemu-nbd does support AIO in the latest versions. There's also > --cache=MODE and --aio=MODE command-line options. Oh true, it's just hidden behind coroutines. With --aio-native and --nocache I actually get fairly reasonable performance out of it now. > But if you need a QMP interface, adding it to qemu-nbd would really be a > bad idea. :) I don't nessecarily need QMP, I'm just looking for a way to create a snapshot underneath an exported image. > You can use qtest mode to get very close to this (even if you send > stop/cont by mistake on the monitor, no code will actually run): > > qemu-system-x86_64 -chardev file,id=null,path=/dev/null -qtest null > -machine accel=qtest -m 1 -nodefaults -nographic > > but having a separate do-nothing target would probably be nicer... > though Anthony may have different opinions. That looks fairlt reasonable, thanks.
diff --git a/Makefile.objs b/Makefile.objs index 124f783..40384ff 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -61,7 +61,7 @@ endif # suppress *all* target specific code in case of system emulation, i.e. a # single QEMU executable should support all CPUs and machines. -common-obj-y = $(block-obj-y) blockdev.o +common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o common-obj-y += net.o net/ common-obj-y += qom/ common-obj-y += readline.o console.o cursor.o diff --git a/blockdev-nbd.c b/blockdev-nbd.c new file mode 100644 index 0000000..8031813 --- /dev/null +++ b/blockdev-nbd.c @@ -0,0 +1,119 @@ +/* + * Serving QEMU block devices via NBD + * + * Copyright (c) 2012 Red Hat, Inc. + * + * Author: Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "blockdev.h" +#include "hw/block-common.h" +#include "monitor.h" +#include "qerror.h" +#include "sysemu.h" +#include "qmp-commands.h" +#include "trace.h" +#include "nbd.h" +#include "qemu_socket.h" + +static int server_fd = -1; + +static void nbd_accept(void *opaque) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + + int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); + if (fd >= 0) { + nbd_client_new(NULL, fd, nbd_client_put); + } +} + +void qmp_nbd_server_start(SocketAddress *addr, Error **errp) +{ + if (server_fd != -1) { + error_setg(errp, "NBD server already running"); + return; + } + + server_fd = socket_listen(addr, errp); + if (server_fd != -1) { + qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL); + } +} + +/* Hook into the BlockDriverState notifiers to close the export when + * the file is closed. + */ +typedef struct NBDCloseNotifier { + Notifier n; + NBDExport *exp; + QTAILQ_ENTRY(NBDCloseNotifier) next; +} NBDCloseNotifier; + +static QTAILQ_HEAD(, NBDCloseNotifier) close_notifiers = + QTAILQ_HEAD_INITIALIZER(close_notifiers); + +static void nbd_close_notifier(Notifier *n, void *data) +{ + NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n); + + notifier_remove(&cn->n); + QTAILQ_REMOVE(&close_notifiers, cn, next); + + nbd_export_close(cn->exp); + nbd_export_put(cn->exp); + g_free(cn); +} + +static void nbd_server_put_ref(NBDExport *exp) +{ + BlockDriverState *bs = nbd_export_get_blockdev(exp); + drive_put_ref(drive_get_by_blockdev(bs)); +} + +void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, + Error **errp) +{ + BlockDriverState *bs; + NBDExport *exp; + NBDCloseNotifier *n; + + if (nbd_export_find(device)) { + error_setg(errp, "NBD server already exporting device '%s'", device); + return; + } + + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, + nbd_server_put_ref); + + nbd_export_set_name(exp, device); + drive_get_ref(drive_get_by_blockdev(bs)); + + n = g_malloc0(sizeof(NBDCloseNotifier)); + n->n.notify = nbd_close_notifier; + n->exp = exp; + bdrv_add_close_notifier(bs, &n->n); + QTAILQ_INSERT_TAIL(&close_notifiers, n, next); +} + +void qmp_nbd_server_stop(Error **errp) +{ + while (!QTAILQ_EMPTY(&close_notifiers)) { + NBDCloseNotifier *cn = QTAILQ_FIRST(&close_notifiers); + nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp)); + } + + qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL); + close(server_fd); + server_fd = -1; +} diff --git a/qapi-schema.json b/qapi-schema.json index b443a99..229421d 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2697,3 +2697,46 @@ # Since: 0.14.0 ## { 'command': 'screendump', 'data': {'filename': 'str'} } + +## +# @nbd-server-start: +# +# Start an NBD server listening on the given host and port. Block +# devices can then be exported using @nbd-server-add. The NBD +# server will present them as named exports; for example, another +# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME". +# +# @addr: Address on which to listen. +# +# Returns: error if the server is already running. +# +# Since: 1.3.0 +## +{ 'command': 'nbd-server-start', + 'data': { 'addr': 'SocketAddress' } } + +## +# @nbd-server-add: +# +# Export a device to QEMU's embedded NBD server. +# +# @device: Block device to be exported +# +# @writable: Whether clients should be able to write to the device via the +# NBD connection (default false). #optional +# +# Returns: error if the device is already marked for export. +# +# Since: 1.3.0 +## +{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool'} } + +## +# @nbd-server-stop: +# +# Stop QEMU's embedded NBD server, and unregister all devices previously +# added via @nbd-server-add. +# +# Since: 1.3.0 +## +{ 'command': 'nbd-server-stop' } diff --git a/qmp-commands.hx b/qmp-commands.hx index 36e08d9..c394af0 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -2503,6 +2503,22 @@ EQMP }, { + .name = "nbd-server-start", + .args_type = "addr:q", + .mhandler.cmd_new = qmp_marshal_input_nbd_server_start, + }, + { + .name = "nbd-server-add", + .args_type = "device:B,writable:b?", + .mhandler.cmd_new = qmp_marshal_input_nbd_server_add, + }, + { + .name = "nbd-server-stop", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_nbd_server_stop, + }, + + { .name = "change-vnc-password", .args_type = "password:s", .mhandler.cmd_new = qmp_marshal_input_change_vnc_password,
Adding an NBD server inside QEMU is trivial, since all the logic is in nbd.c and can be shared easily between qemu-nbd and QEMU itself. The main difference is that qemu-nbd serves a single unnamed export, while QEMU serves named exports. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- Makefile.objs | 2 +- blockdev-nbd.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ qapi-schema.json | 43 ++++++++++++++++++++ qmp-commands.hx | 16 ++++++++ 4 file modificati, 179 inserzioni(+). 1 rimozione(-) create mode 100644 blockdev-nbd.c