Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- blockdev-nbd.c | 38 ++++++++++++++++++++++++++++++++++++++ qapi/opts-visitor.c | 48 ++++++++++++++++++++---------------------------- 2 file modificati, 58 inserzioni(+), 28 rimozioni(-)
diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 5a415be..c190caa 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -62,12 +62,39 @@ void qmp_nbd_server_start(IPSocketAddress *addr, Error **errp) qemu_opts_del(opts); } +/* 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_remove(NBDCloseNotifier *cn) +{ + notifier_remove(&cn->n); + QTAILQ_REMOVE(&close_notifiers, cn, next); + g_free(cn); +} + +static void nbd_close_notifier(Notifier *n, void *data) +{ + NBDCloseNotifier *cn = DO_UPCAST(NBDCloseNotifier, n, n); + + nbd_export_close(cn->exp); + nbd_close_notifier_remove(cn); +} void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, Error **errp) { BlockDriverState *bs; NBDExport *exp; + NBDCloseNotifier *n; bs = bdrv_find(device); if (!bs) { @@ -82,10 +109,21 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY); nbd_export_set_name(exp, device); + + 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_remove(cn); + } + nbd_export_close_all(); qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL); close(server_fd); diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index a59d306..6893d62 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -20,9 +20,6 @@ struct OptsVisitor { Visitor visitor; - /* Ownership remains with opts_visitor_new()'s caller. */ - const QemuOpts *opts_root; - unsigned depth; /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value @@ -36,9 +33,9 @@ struct OptsVisitor GQueue *repeated_opts; bool repeated_opts_first; - /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for - * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does - * not survive or escape the OptsVisitor object. + /* If the "id" is set on the QemuOpts, reinstantiate it as a fake QemuOpt + * for uniformity. Only its "name" and "str" fields are set. "fake_id_opt" + * does not survive or escape the OptsVisitor object. */ QemuOpt *fake_id_opt; }; @@ -77,29 +74,9 @@ opts_start_struct(Visitor *v, void **obj, const char *kind, const char *name, size_t size, Error **errp) { OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); - const QemuOpt *opt; *obj = g_malloc0(size > 0 ? size : 1); - if (ov->depth++ > 0) { - return; - } - - ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal, - NULL, &destroy_list); - QTAILQ_FOREACH(opt, &ov->opts_root->head, next) { - /* ensured by qemu-option.c::opts_do_parse() */ - assert(strcmp(opt->name, "id") != 0); - - opts_visitor_insert(ov->unprocessed_opts, opt); - } - - if (ov->opts_root->id != NULL) { - ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt); - - ov->fake_id_opt->name = "id"; - ov->fake_id_opt->str = ov->opts_root->id; - opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt); - } + ov->depth++; } @@ -372,6 +349,7 @@ OptsVisitor * opts_visitor_new(const QemuOpts *opts) { OptsVisitor *ov; + const QemuOpt *opt; ov = g_malloc0(sizeof *ov); @@ -403,8 +381,22 @@ opts_visitor_new(const QemuOpts *opts) ov->visitor.start_optional = &opts_start_optional; - ov->opts_root = opts; + ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal, + NULL, &destroy_list); + QTAILQ_FOREACH(opt, &opts->head, next) { + /* ensured by qemu-option.c::opts_do_parse() */ + assert(strcmp(opt->name, "id") != 0); + opts_visitor_insert(ov->unprocessed_opts, opt); + } + + if (opts->id != NULL) { + ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt); + + ov->fake_id_opt->name = "id"; + ov->fake_id_opt->str = opts->id; + opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt); + } return ov; } -- 1.7.11.2