diff mbox series

[15/18] xen: add a mechanism to automatically create XenDevice-s...

Message ID 20181121151211.15997-16-paul.durrant@citrix.com
State New
Headers show
Series Xen PV backend 'qdevification' | expand

Commit Message

Paul Durrant Nov. 21, 2018, 3:12 p.m. UTC
...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 <paul.durrant@citrix.com>
---
Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Anthony Perard <anthony.perard@citrix.com>
---
 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

Comments

Anthony PERARD Dec. 4, 2018, 3:35 p.m. UTC | #1
On Wed, Nov 21, 2018 at 03:12:08PM +0000, 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,
> +                          xendev, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        error_prepend(errp, "failed to watch backend online: ");

You probably want a return here, in case there is more code added after.

> +    }

Other instances of error_propagate;error_prepend to be replaced by
error_propagate_prepend.

>  }
>  

Thanks,
Paul Durrant Dec. 6, 2018, 12:36 p.m. UTC | #2
> -----Original Message-----
> From: Anthony PERARD [mailto:anthony.perard@citrix.com]
> Sent: 04 December 2018 15:35
> To: Paul Durrant <Paul.Durrant@citrix.com>
> Cc: qemu-block@nongnu.org; qemu-devel@nongnu.org; xen-
> devel@lists.xenproject.org; Stefano Stabellini <sstabellini@kernel.org>
> Subject: Re: [PATCH 15/18] xen: add a mechanism to automatically create
> XenDevice-s...
> 
> On Wed, Nov 21, 2018 at 03:12:08PM +0000, 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->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,
> > +                          xendev, &local_err);
> > +    if (local_err) {
> > +        error_propagate(errp, local_err);
> > +        error_prepend(errp, "failed to watch backend online: ");
> 
> You probably want a return here, in case there is more code added after.

Yes, there should be returns in all three cases above.

> 
> > +    }
> 
> Other instances of error_propagate;error_prepend to be replaced by
> error_propagate_prepend.

Yes, will do.

  Paul

> 
> >  }
> >
> 
> Thanks,
> 
> --
> Anthony PERARD
Anthony PERARD Dec. 6, 2018, 3:24 p.m. UTC | #3
On Thu, Dec 06, 2018 at 12:36:52PM +0000, Paul Durrant wrote:
> > -----Original Message-----
> > From: Anthony PERARD [mailto:anthony.perard@citrix.com]
> > Sent: 04 December 2018 15:35
> > 
> > On Wed, Nov 21, 2018 at 03:12:08PM +0000, 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.
Paul Durrant Dec. 6, 2018, 3:36 p.m. UTC | #4
> -----Original Message-----
> From: Anthony PERARD [mailto:anthony.perard@citrix.com]
> Sent: 06 December 2018 15:24
> To: Paul Durrant <Paul.Durrant@citrix.com>
> Cc: qemu-block@nongnu.org; qemu-devel@nongnu.org; xen-
> devel@lists.xenproject.org; Stefano Stabellini <sstabellini@kernel.org>
> Subject: Re: [PATCH 15/18] xen: add a mechanism to automatically create
> XenDevice-s...
> 
> On Thu, Dec 06, 2018 at 12:36:52PM +0000, Paul Durrant wrote:
> > > -----Original Message-----
> > > From: Anthony PERARD [mailto:anthony.perard@citrix.com]
> > > Sent: 04 December 2018 15:35
> > >
> > > On Wed, Nov 21, 2018 at 03:12:08PM +0000, 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
diff mbox series

Patch

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 0000000000..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
index c4b253103f..e58b891c93 100644
--- a/hw/xen/xen-bus.c
+++ b/hw/xen/xen-bus.c
@@ -10,10 +10,12 @@ 
 #include "hw/hw.h"
 #include "hw/sysbus.h"
 #include "hw/xen/xen.h"
+#include "hw/xen/xen-backend.h"
 #include "hw/xen/xen-bus.h"
 #include "hw/xen/xen-bus-helper.h"
 #include "monitor/monitor.h"
 #include "qapi/error.h"
+#include "qapi/qmp/qdict.h"
 #include "sysemu/sysemu.h"
 #include "trace.h"
 
@@ -132,12 +134,118 @@  static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch)
     g_free(watch);
 }
 
+static void xen_bus_backend_create(XenBus *xenbus, const char *type,
+                                   const char *name, char *path)
+{
+    char **key;
+    QDict *opts;
+    unsigned int i, n;
+    Error *local_err = NULL;
+
+    trace_xen_bus_backend_create(type, path);
+
+    key = xs_directory(xenbus->xsh, XBT_NULL, path, &n);
+    if (!key) {
+        return;
+    }
+
+    opts = qdict_new();
+    for (i = 0; i < n; i++) {
+        char *val;
+
+        /*
+         * Assume anything found in the xenstore backend area, other than
+         * the keys created for a generic XenDevice, are parameters
+         * to be used to configure the backend.
+         */
+        if (!strcmp(key[i], "state") ||
+            !strcmp(key[i], "online") ||
+            !strcmp(key[i], "frontend") ||
+            !strcmp(key[i], "frontend-id") ||
+            !strcmp(key[i], "hotplug-status"))
+            continue;
+
+        if (xs_node_scanf(xenbus->xsh, path, key[i], "%ms", &val) == 1) {
+            qdict_put_str(opts, key[i], val);
+            free(val);
+        }
+    }
+
+    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);
+    }
+}
+
+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) {
+        return;
+    }
+
+    for (i = 0; i < n; i++) {
+        char *backend_path = g_strdup_printf("%s/%s", domain_path,
+                                             backend[i]);
+        enum xenbus_state backend_state;
+
+        if (xs_node_scanf(xenbus->xsh, backend_path, "state", "%u",
+                          &backend_state) != 1)
+            backend_state = XenbusStateUnknown;
+
+        if (backend_state == XenbusStateInitialising) {
+            xen_bus_backend_create(xenbus, type, backend[i], backend_path);
+        }
+
+        g_free(backend_path);
+    }
+
+    free(backend);
+    g_free(domain_path);
+}
+
+static void xen_bus_enumerate(void *opaque)
+{
+    XenBus *xenbus = opaque;
+    char **type;
+    unsigned int i, n;
+
+    trace_xen_bus_enumerate();
+
+    type = xs_directory(xenbus->xsh, XBT_NULL, "backend", &n);
+    if (!type) {
+        return;
+    }
+
+    for (i = 0; i < n; i++) {
+        xen_bus_type_enumerate(xenbus, type[i]);
+    }
+
+    free(type);
+}
+
 static void xen_bus_unrealize(BusState *bus, Error **errp)
 {
     XenBus *xenbus = XEN_BUS(bus);
 
     trace_xen_bus_unrealize();
 
+    if (xenbus->backend_watch) {
+        xen_bus_remove_watch(xenbus, xenbus->backend_watch);
+    }
+
     if (!xenbus->xsh) {
         return;
     }
@@ -174,6 +282,7 @@  static void xen_bus_realize(BusState *bus, Error **errp)
 {
     XenBus *xenbus = XEN_BUS(bus);
     unsigned int domid;
+    Error *local_err = NULL;
 
     trace_xen_bus_realize();
 
@@ -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: ");
+    }
+
     return;
 
 fail:
@@ -268,6 +388,60 @@  enum xenbus_state xen_device_backend_get_state(XenDevice *xendev)
     return xendev->backend_state;
 }
 
+static void xen_device_backend_set_online(XenDevice *xendev, bool online)
+{
+    const char *type = object_get_typename(OBJECT(xendev));
+
+    if (xendev->backend_online == online) {
+        return;
+    }
+
+    trace_xen_device_backend_online(type, xendev->name, online);
+
+    xendev->backend_online = online;
+    xen_device_backend_printf(xendev, "online", "%u", online);
+}
+
+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);
+
+    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.
+     */
+    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: ");
     }
+
+    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: ");
+    }
+
+    xendev->backend_online_watch =
+        xen_bus_add_watch(xenbus, xendev->backend_path,
+                          "online", xen_device_backend_changed,
+                          xendev, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "failed to watch backend online: ");
+    }
 }
 
 static void xen_device_backend_destroy(XenDevice *xendev)
 {
     XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
 
+    if (xendev->backend_online_watch) {
+        xen_bus_remove_watch(xenbus, xendev->backend_online_watch);
+    }
+
+    if (xendev->backend_state_watch) {
+        xen_bus_remove_watch(xenbus, xendev->backend_state_watch);
+    }
+
     if (!xendev->backend_path) {
         return;
     }
@@ -378,24 +578,6 @@  static void xen_device_frontend_changed(void *opaque)
             error_free(local_err);
         }
     }
-
-    /*
-     * 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 (xendev->backend_state == XenbusStateClosed) {
-        unsigned int online;
-
-        if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) {
-            online = 0;
-        }
-
-        if (online) {
-            xen_device_backend_set_state(xendev, XenbusStateInitWait);
-        }
-    }
 }
 
 static void xen_device_frontend_create(XenDevice *xendev, Error **errp)
@@ -764,9 +946,9 @@  static void xen_device_realize(DeviceState *dev, Error **errp)
                               xendev->frontend_path);
     xen_device_backend_printf(xendev, "frontend-id", "%u",
                               xendev->frontend_id);
-    xen_device_backend_printf(xendev, "online", "%u", 1);
     xen_device_backend_printf(xendev, "hotplug-status", "connected");
 
+    xen_device_backend_set_online(xendev, true);
     xen_device_backend_set_state(xendev, XenbusStateInitWait);
 
     xen_device_frontend_printf(xendev, "backend", "%s",
diff --git a/include/hw/xen/xen-backend.h b/include/hw/xen/xen-backend.h
new file mode 100644
index 0000000000..ca5ae34398
--- /dev/null
+++ b/include/hw/xen/xen-backend.h
@@ -0,0 +1,24 @@ 
+/*
+ * Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ */
+
+#ifndef HW_XEN_BACKEND_H
+#define HW_XEN_BACKEND_H
+
+#include "hw/xen/xen-bus.h"
+
+typedef void (*XenBackendDeviceCreate)(BusState *bus, const char *name,
+                                       QDict *opts, Error **errp);
+
+typedef struct XenBackendInfo {
+    const char *type;
+    XenBackendDeviceCreate create;
+} XenBackendInfo;
+
+void xen_backend_register(const XenBackendInfo *info);
+
+void xen_backend_device_create(BusState *bus, const char *type,
+                               const char *name, QDict *opts, Error **errp);
+
+#endif /* HW_XEN_BACKEND_H */
diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h
index d931e01268..48f3e068d3 100644
--- a/include/hw/xen/xen-bus.h
+++ b/include/hw/xen/xen-bus.h
@@ -21,7 +21,9 @@  typedef struct XenDevice {
     char *backend_path, *frontend_path;
     enum xenbus_state backend_state, frontend_state;
     Notifier exit;
-    XenWatch *frontend_state_watch;
+    XenWatch *backend_state_watch, *frontend_state_watch;
+    bool backend_online;
+    XenWatch *backend_online_watch;
     xengnttab_handle *xgth;
     bool feature_grant_copy;
     xenevtchn_handle *xeh;
@@ -61,6 +63,7 @@  typedef struct XenBus {
     domid_t backend_id;
     struct xs_handle *xsh;
     NotifierList watch_notifiers;
+    XenWatch *backend_watch;
 } XenBus;
 
 typedef struct XenBusClass {
diff --git a/include/qemu/module.h b/include/qemu/module.h
index 54300ab6e5..55dd2beea8 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -44,6 +44,7 @@  typedef enum {
     MODULE_INIT_OPTS,
     MODULE_INIT_QOM,
     MODULE_INIT_TRACE,
+    MODULE_INIT_XEN_BACKEND,
     MODULE_INIT_MAX
 } module_init_type;
 
@@ -51,6 +52,8 @@  typedef enum {
 #define opts_init(function) module_init(function, MODULE_INIT_OPTS)
 #define type_init(function) module_init(function, MODULE_INIT_QOM)
 #define trace_init(function) module_init(function, MODULE_INIT_TRACE)
+#define xen_backend_init(function) module_init(function, \
+                                               MODULE_INIT_XEN_BACKEND)
 
 #define block_module_load_one(lib) module_load_one("block-", lib)
 #define ui_module_load_one(lib) module_load_one("ui-", lib)