Re: [Qemu-block] [PATCH 15/18] xen: add a mechanism to automatically create XenDevice-s...
> -Original Message- > From: Anthony PERARD [mailto:anthony.per...@citrix.com] > Sent: 06 December 2018 15:24 > To: Paul Durrant > Cc: qemu-block@nongnu.org; qemu-de...@nongnu.org; xen- > de...@lists.xenproject.org; Stefano Stabellini > Subject: Re: [PATCH 15/18] xen: add a mechanism to automatically create > XenDevice-s... > > On Thu, Dec 06, 2018 at 12:36:52PM +, Paul Durrant wrote: > > > -Original Message- > > > From: Anthony PERARD [mailto:anthony.per...@citrix.com] > > > Sent: 04 December 2018 15:35 > > > > > > On Wed, Nov 21, 2018 at 03:12:08PM +, Paul Durrant wrote: > > > > +xenbus->backend_watch = > > > > +xen_bus_add_watch(xenbus, "", /* domain root node */ > > > > + "backend", xen_bus_enumerate, xenbus, > > > &local_err); > > > > +if (local_err) { > > > > +error_propagate(errp, local_err); > > > > +error_prepend(errp, "failed to set up enumeration watch: > "); > > > > > > You should use error_propagate_prepend instead > > > error_propagate;error_prepend. And it looks like there is the same > > > mistake in other patches that I haven't notice. > > > > > > > Oh, I didn't know about that one either... I've only seen the separate > calls used elsewhere. > > That information is all in "include/qapi/error.h", if you which to know > more on how to use Error. > Thanks. > > > Also you probably want goto fail here. > > > > > > > Not sure about that. Whilst the bus scan won't happen, it doesn't mean > devices can't be added via QMP. > > In that case, don't modify errp, and use error_reportf_err instead, or > warn_reportf_err (then local_err = NULL, in case it is reused in a > future modification of the function). > > Setting errp (with error_propagate) means that the function failed, and > QEMU is going to exit(1), because of qdev_init_nofail call in > xen_bus_init. Ah, good point. I'll wait for more feedback on v2 and then fix in v3. Paul
Re: [Qemu-block] [PATCH 15/18] xen: add a mechanism to automatically create XenDevice-s...
On Thu, Dec 06, 2018 at 12:36:52PM +, Paul Durrant wrote: > > -Original Message- > > From: Anthony PERARD [mailto:anthony.per...@citrix.com] > > Sent: 04 December 2018 15:35 > > > > On Wed, Nov 21, 2018 at 03:12:08PM +, Paul Durrant wrote: > > > +xenbus->backend_watch = > > > +xen_bus_add_watch(xenbus, "", /* domain root node */ > > > + "backend", xen_bus_enumerate, xenbus, > > &local_err); > > > +if (local_err) { > > > +error_propagate(errp, local_err); > > > +error_prepend(errp, "failed to set up enumeration watch: "); > > > > You should use error_propagate_prepend instead > > error_propagate;error_prepend. And it looks like there is the same > > mistake in other patches that I haven't notice. > > > > Oh, I didn't know about that one either... I've only seen the separate calls > used elsewhere. That information is all in "include/qapi/error.h", if you which to know more on how to use Error. > > Also you probably want goto fail here. > > > > Not sure about that. Whilst the bus scan won't happen, it doesn't mean > devices can't be added via QMP. In that case, don't modify errp, and use error_reportf_err instead, or warn_reportf_err (then local_err = NULL, in case it is reused in a future modification of the function). Setting errp (with error_propagate) means that the function failed, and QEMU is going to exit(1), because of qdev_init_nofail call in xen_bus_init. > > > +static void xen_device_backend_changed(void *opaque) > > > +{ > > > +XenDevice *xendev = opaque; > > > +const char *type = object_get_typename(OBJECT(xendev)); > > > +enum xenbus_state state; > > > +unsigned int online; > > > + > > > +trace_xen_device_backend_changed(type, xendev->name); > > > + > > > +if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) { > > > +state = XenbusStateUnknown; > > > +} > > > + > > > +xen_device_backend_set_state(xendev, state); > > > > It's kind of weird to set the internal state base on the external one > > that something else may have modified. Shouldn't we check that it is > > fine for something else to modify the state and that it is a correct > > transition? > > The only thing (apart from this code) that's going to have perms to write the > backend state is the toolstack... which is, of course, be definition trusted. "trusted" doesn't mean that there isn't a bug somewhere else :-). But I guess it's good enough for now. > > Also aren't we going in a loop by having QEMU set the state, then the > > watch fires again? (Not really a loop since the function _set_state > > check for changes. > > No. It's de-bounced inside the set_state function. > > > > > Also maybe we should watch for the state changes only when something > > else like libxl creates (ask for) the backend, and ignore changes when > > QEMU did it itself. > > I don't think it's necessary to add that complexity. Ok. -- Anthony PERARD
Re: [Qemu-block] [PATCH 15/18] xen: add a mechanism to automatically create XenDevice-s...
> -Original Message- > From: Anthony PERARD [mailto:anthony.per...@citrix.com] > Sent: 04 December 2018 15:35 > To: Paul Durrant > Cc: qemu-block@nongnu.org; qemu-de...@nongnu.org; xen- > de...@lists.xenproject.org; Stefano Stabellini > Subject: Re: [PATCH 15/18] xen: add a mechanism to automatically create > XenDevice-s... > > On Wed, Nov 21, 2018 at 03:12:08PM +, Paul Durrant wrote: > > +xen_backend_device_create(BUS(xenbus), type, name, opts, > &local_err); > > +qobject_unref(opts); > > + > > +if (local_err) { > > +const char *msg = error_get_pretty(local_err); > > + > > +error_report("failed to create '%s' device '%s': %s", type, > name, > > + msg); > > +error_free(local_err); > > You can use error_reportf_err() instead of those three calls. I may have > only suggest error_report_err in a previous patch, but error_reportf_err > does the error_prepend as well. > Ah. I'll go back over the patches and use that where necessary. > > +} > > +} > > + > > +static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) > > +{ > > +char *domain_path = g_strdup_printf("backend/%s/%u", type, > xen_domid); > > +char **backend; > > +unsigned int i, n; > > + > > +trace_xen_bus_type_enumerate(type); > > + > > +backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); > > +if (!backend) { > > domain_path isn't free here, you probably want a `goto out` which would > free everything. Ok. > > > +return; > > +} > > + > > @@ -193,6 +302,17 @@ static void xen_bus_realize(BusState *bus, Error > **errp) > > notifier_list_init(&xenbus->watch_notifiers); > > qemu_set_fd_handler(xs_fileno(xenbus->xsh), xen_bus_watch, NULL, > > xenbus); > > + > > +module_call_init(MODULE_INIT_XEN_BACKEND); > > + > > +xenbus->backend_watch = > > +xen_bus_add_watch(xenbus, "", /* domain root node */ > > + "backend", xen_bus_enumerate, xenbus, > &local_err); > > +if (local_err) { > > +error_propagate(errp, local_err); > > +error_prepend(errp, "failed to set up enumeration watch: "); > > You should use error_propagate_prepend instead > error_propagate;error_prepend. And it looks like there is the same > mistake in other patches that I haven't notice. > Oh, I didn't know about that one either... I've only seen the separate calls used elsewhere. > Also you probably want goto fail here. > Not sure about that. Whilst the bus scan won't happen, it doesn't mean devices can't be added via QMP. > > > +static void xen_device_backend_changed(void *opaque) > > +{ > > +XenDevice *xendev = opaque; > > +const char *type = object_get_typename(OBJECT(xendev)); > > +enum xenbus_state state; > > +unsigned int online; > > + > > +trace_xen_device_backend_changed(type, xendev->name); > > + > > +if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) { > > +state = XenbusStateUnknown; > > +} > > + > > +xen_device_backend_set_state(xendev, state); > > It's kind of weird to set the internal state base on the external one > that something else may have modified. Shouldn't we check that it is > fine for something else to modify the state and that it is a correct > transition? The only thing (apart from this code) that's going to have perms to write the backend state is the toolstack... which is, of course, be definition trusted. > > Also aren't we going in a loop by having QEMU set the state, then the > watch fires again? (Not really a loop since the function _set_state > check for changes. No. It's de-bounced inside the set_state function. > > Also maybe we should watch for the state changes only when something > else like libxl creates (ask for) the backend, and ignore changes when > QEMU did it itself. I don't think it's necessary to add that complexity. > > > + > > +if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) > { > > +online = 0; > > +} > > + > > +xen_device_backend_set_online(xendev, !!online); > > + > > +/* > > + * If a backend is still 'online' then its state should be cycled > > + * back round to InitWait in order for a new frontend instance to > > + * connect. This may happen when, for example, a frontend driver is > > + * re-installed or updated. > > + * If a backend id not 'online' then the device should be > destroyed. > > s/id/is/ Ok. > > > + */ > > +if (xendev->backend_online && > > +xendev->backend_state == XenbusStateClosed) { > > +xen_device_backend_set_state(xendev, XenbusStateInitWait); > > +} else if (!xendev->backend_online && > > + (xendev->backend_state == XenbusStateClosed || > > +xendev->backend_state == XenbusStateInitialising || > > +xendev->backend_state == XenbusStateInitWait || > > +xendev->backe
Re: [Qemu-block] [PATCH 15/18] xen: add a mechanism to automatically create XenDevice-s...
On Wed, Nov 21, 2018 at 03:12:08PM +, Paul Durrant wrote: > +xen_backend_device_create(BUS(xenbus), type, name, opts, &local_err); > +qobject_unref(opts); > + > +if (local_err) { > +const char *msg = error_get_pretty(local_err); > + > +error_report("failed to create '%s' device '%s': %s", type, name, > + msg); > +error_free(local_err); You can use error_reportf_err() instead of those three calls. I may have only suggest error_report_err in a previous patch, but error_reportf_err does the error_prepend as well. > +} > +} > + > +static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) > +{ > +char *domain_path = g_strdup_printf("backend/%s/%u", type, xen_domid); > +char **backend; > +unsigned int i, n; > + > +trace_xen_bus_type_enumerate(type); > + > +backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); > +if (!backend) { domain_path isn't free here, you probably want a `goto out` which would free everything. > +return; > +} > + > @@ -193,6 +302,17 @@ static void xen_bus_realize(BusState *bus, Error **errp) > notifier_list_init(&xenbus->watch_notifiers); > qemu_set_fd_handler(xs_fileno(xenbus->xsh), xen_bus_watch, NULL, > xenbus); > + > +module_call_init(MODULE_INIT_XEN_BACKEND); > + > +xenbus->backend_watch = > +xen_bus_add_watch(xenbus, "", /* domain root node */ > + "backend", xen_bus_enumerate, xenbus, &local_err); > +if (local_err) { > +error_propagate(errp, local_err); > +error_prepend(errp, "failed to set up enumeration watch: "); You should use error_propagate_prepend instead error_propagate;error_prepend. And it looks like there is the same mistake in other patches that I haven't notice. Also you probably want goto fail here. > +static void xen_device_backend_changed(void *opaque) > +{ > +XenDevice *xendev = opaque; > +const char *type = object_get_typename(OBJECT(xendev)); > +enum xenbus_state state; > +unsigned int online; > + > +trace_xen_device_backend_changed(type, xendev->name); > + > +if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) { > +state = XenbusStateUnknown; > +} > + > +xen_device_backend_set_state(xendev, state); It's kind of weird to set the internal state base on the external one that something else may have modified. Shouldn't we check that it is fine for something else to modify the state and that it is a correct transition? Also aren't we going in a loop by having QEMU set the state, then the watch fires again? (Not really a loop since the function _set_state check for changes. Also maybe we should watch for the state changes only when something else like libxl creates (ask for) the backend, and ignore changes when QEMU did it itself. > + > +if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) { > +online = 0; > +} > + > +xen_device_backend_set_online(xendev, !!online); > + > +/* > + * If a backend is still 'online' then its state should be cycled > + * back round to InitWait in order for a new frontend instance to > + * connect. This may happen when, for example, a frontend driver is > + * re-installed or updated. > + * If a backend id not 'online' then the device should be destroyed. s/id/is/ > + */ > +if (xendev->backend_online && > +xendev->backend_state == XenbusStateClosed) { > +xen_device_backend_set_state(xendev, XenbusStateInitWait); > +} else if (!xendev->backend_online && > + (xendev->backend_state == XenbusStateClosed || > +xendev->backend_state == XenbusStateInitialising || > +xendev->backend_state == XenbusStateInitWait || > +xendev->backend_state == XenbusStateUnknown)) { > +object_unparent(OBJECT(xendev)); > +} > +} > + > static void xen_device_backend_create(XenDevice *xendev, Error **errp) > { > XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); > @@ -289,12 +463,38 @@ static void xen_device_backend_create(XenDevice > *xendev, Error **errp) > error_propagate(errp, local_err); > error_prepend(errp, "failed to create backend: "); It looks like there is a missing return here. > } > + > +xendev->backend_state_watch = > +xen_bus_add_watch(xenbus, xendev->backend_path, > + "state", xen_device_backend_changed, > + xendev, &local_err); > +if (local_err) { > +error_propagate(errp, local_err); > +error_prepend(errp, "failed to watch backend state: "); You should return here, as local_err mustn't be reused. > +} > + > +xendev->backend_online_watch = > +xen_bus_add_watch(xenbus, xendev->backend_path, > + "online", xen_device_backend_changed, > +
[Qemu-block] [PATCH 15/18] xen: add a mechanism to automatically create XenDevice-s...
...that maintains compatibility with existing Xen toolstacks. Xen toolstacks instantiate PV backends by simply writing information into xenstore and expecting a backend implementation to be watching for this. This patch adds a new 'xen-backend' module to allow individual XenDevice implementations to register a creator function to be called when a tool- stack instantiates a new backend in this way. To support this it is also necessary to add new watchers into the XenBus implementation to handle enumeration of new backends and also destruction of XenDevice-s when the toolstack sets the backend 'online' key to 0. NOTE: This patch only adds the framework. A subsequent patch will add a creator function for xen-qdisk. Signed-off-by: Paul Durrant --- Cc: Stefano Stabellini Cc: Anthony Perard --- hw/xen/Makefile.objs | 2 +- hw/xen/trace-events | 5 + hw/xen/xen-backend.c | 67 + hw/xen/xen-bus.c | 220 +++ include/hw/xen/xen-backend.h | 24 + include/hw/xen/xen-bus.h | 5 +- include/qemu/module.h| 3 + 7 files changed, 305 insertions(+), 21 deletions(-) create mode 100644 hw/xen/xen-backend.c create mode 100644 include/hw/xen/xen-backend.h diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs index 77c0868190..84df60a928 100644 --- a/hw/xen/Makefile.objs +++ b/hw/xen/Makefile.objs @@ -1,5 +1,5 @@ # xen backend driver support -common-obj-$(CONFIG_XEN) += xen-legacy-backend.o xen_devconfig.o xen_pvdev.o xen-common.o xen-bus.o xen-bus-helper.o +common-obj-$(CONFIG_XEN) += xen-legacy-backend.o xen_devconfig.o xen_pvdev.o xen-common.o xen-bus.o xen-bus-helper.o xen-backend.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_graphics.o xen_pt_msi.o diff --git a/hw/xen/trace-events b/hw/xen/trace-events index 94c46c2e34..5b12135083 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -16,11 +16,16 @@ xen_domid_restrict(int err) "err: %u" # include/hw/xen/xen-bus.c xen_bus_realize(void) "" xen_bus_unrealize(void) "" +xen_bus_enumerate(void) "" +xen_bus_type_enumerate(const char *type) "type: %s" +xen_bus_backend_create(const char *type, const char *path) "type: %s path: %s" xen_bus_add_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s" xen_bus_remove_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s" xen_bus_watch(const char *token) "token: %s" xen_device_realize(const char *type, char *name) "type: %s name: %s" xen_device_unrealize(const char *type, char *name) "type: %s name: %s" xen_device_backend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s" +xen_device_backend_online(const char *type, char *name, bool online) "type: %s name: %s -> %u" xen_device_frontend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s" +xen_device_backend_changed(const char *type, char *name) "type: %s name: %s" xen_device_frontend_changed(const char *type, char *name) "type: %s name: %s" diff --git a/hw/xen/xen-backend.c b/hw/xen/xen-backend.c new file mode 100644 index 00..7581fd390d --- /dev/null +++ b/hw/xen/xen-backend.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) Citrix Systems Inc. + * All rights reserved. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "hw/xen/xen-backend.h" + +typedef struct XenBackendImpl { +const char *type; +XenBackendDeviceCreate create; +} XenBackendImpl; + +static GHashTable *xen_backend_table_get(void) +{ +static GHashTable *table; + +if (table == NULL) { +table = g_hash_table_new(g_str_hash, g_str_equal); +} + +return table; +} + +static void xen_backend_table_add(XenBackendImpl *impl) +{ +g_hash_table_insert(xen_backend_table_get(), (void *)impl->type, impl); +} + +static XenBackendImpl *xen_backend_table_lookup(const char *type) +{ +return g_hash_table_lookup(xen_backend_table_get(), type); +} + +void xen_backend_register(const XenBackendInfo *info) +{ +XenBackendImpl *impl = g_new0(XenBackendImpl, 1); + +g_assert(info->type); + +if (xen_backend_table_lookup(info->type)) { +error_report("attempt to register duplicate Xen backend type '%s'", + info->type); +abort(); +} + +if (!info->create) { +error_report("backend type '%s' has no creator", info->type); +abort(); +} + +impl->type = info->type; +impl->create = info->create; + +xen_backend_table_add(impl); +} + +void xen_backend_device_create(BusState *bus, const char *type, + const char *name, QDict *opts, Error **errp) +{ +XenBackendImpl *impl = xen_backend_table_lookup(type); + +if (impl) { +impl->create(bus, name, opts, errp); +} +} diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c in