diff options
Diffstat (limited to 'hw/xen/xen-bus.c')
-rw-r--r-- | hw/xen/xen-bus.c | 744 |
1 files changed, 346 insertions, 398 deletions
diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 3aeccec69c..fb82cc33e4 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -7,8 +7,9 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +#include "qemu/module.h" #include "qemu/uuid.h" -#include "hw/hw.h" +#include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "hw/xen/xen.h" #include "hw/xen/xen-backend.h" @@ -18,6 +19,7 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "sysemu/sysemu.h" +#include "net/net.h" #include "trace.h" static char *xen_device_get_backend_path(XenDevice *xendev) @@ -52,34 +54,34 @@ static char *xen_device_get_frontend_path(XenDevice *xendev) static void xen_device_unplug(XenDevice *xendev, Error **errp) { + ERRP_GUARD(); XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); const char *type = object_get_typename(OBJECT(xendev)); - Error *local_err = NULL; xs_transaction_t tid; trace_xen_device_unplug(type, xendev->name); /* Mimic the way the Xen toolstack does an unplug */ again: - tid = xs_transaction_start(xenbus->xsh); + tid = qemu_xen_xs_transaction_start(xenbus->xsh); if (tid == XBT_NULL) { error_setg_errno(errp, errno, "failed xs_transaction_start"); return; } xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "online", - &local_err, "%u", 0); - if (local_err) { + errp, "%u", 0); + if (*errp) { goto abort; } xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "state", - &local_err, "%u", XenbusStateClosing); - if (local_err) { + errp, "%u", XenbusStateClosing); + if (*errp) { goto abort; } - if (!xs_transaction_end(xenbus->xsh, tid, false)) { + if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { if (errno == EAGAIN) { goto again; } @@ -94,8 +96,7 @@ abort: * We only abort if there is already a failure so ignore any error * from ending the transaction. */ - xs_transaction_end(xenbus->xsh, tid, true); - error_propagate(errp, local_err); + qemu_xen_xs_transaction_end(xenbus->xsh, tid, true); } static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent) @@ -111,109 +112,28 @@ static char *xen_bus_get_dev_path(DeviceState *dev) return xen_device_get_backend_path(XEN_DEVICE(dev)); } -struct XenWatch { - char *node, *key; - char *token; - XenWatchHandler handler; - void *opaque; - Notifier notifier; -}; - -static void watch_notify(Notifier *n, void *data) -{ - XenWatch *watch = container_of(n, XenWatch, notifier); - const char *token = data; - - if (!strcmp(watch->token, token)) { - watch->handler(watch->opaque); - } -} - -static XenWatch *new_watch(const char *node, const char *key, - XenWatchHandler handler, void *opaque) -{ - XenWatch *watch = g_new0(XenWatch, 1); - QemuUUID uuid; - - qemu_uuid_generate(&uuid); - - watch->token = qemu_uuid_unparse_strdup(&uuid); - watch->node = g_strdup(node); - watch->key = g_strdup(key); - watch->handler = handler; - watch->opaque = opaque; - watch->notifier.notify = watch_notify; - - return watch; -} - -static void free_watch(XenWatch *watch) -{ - g_free(watch->token); - g_free(watch->key); - g_free(watch->node); - - g_free(watch); -} - -static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node, - const char *key, XenWatchHandler handler, - void *opaque, Error **errp) -{ - XenWatch *watch = new_watch(node, key, handler, opaque); - Error *local_err = NULL; - - trace_xen_bus_add_watch(watch->node, watch->key, watch->token); - - notifier_list_add(&xenbus->watch_notifiers, &watch->notifier); - - xs_node_watch(xenbus->xsh, node, key, watch->token, &local_err); - if (local_err) { - error_propagate(errp, local_err); - - notifier_remove(&watch->notifier); - free_watch(watch); - - return NULL; - } - - return watch; -} - -static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch, - Error **errp) -{ - trace_xen_bus_remove_watch(watch->node, watch->key, watch->token); - - xs_node_unwatch(xenbus->xsh, watch->node, watch->key, watch->token, - errp); - - notifier_remove(&watch->notifier); - free_watch(watch); -} - static void xen_bus_backend_create(XenBus *xenbus, const char *type, const char *name, char *path, Error **errp) { + ERRP_GUARD(); xs_transaction_t tid; char **key; QDict *opts; unsigned int i, n; - Error *local_err = NULL; trace_xen_bus_backend_create(type, path); again: - tid = xs_transaction_start(xenbus->xsh); + tid = qemu_xen_xs_transaction_start(xenbus->xsh); if (tid == XBT_NULL) { error_setg(errp, "failed xs_transaction_start"); return; } - key = xs_directory(xenbus->xsh, tid, path, &n); + key = qemu_xen_xs_directory(xenbus->xsh, tid, path, &n); if (!key) { - if (!xs_transaction_end(xenbus->xsh, tid, true)) { + if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, true)) { error_setg_errno(errp, errno, "failed xs_transaction_end"); } return; @@ -244,7 +164,7 @@ again: free(key); - if (!xs_transaction_end(xenbus->xsh, tid, false)) { + if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { qobject_unref(opts); if (errno == EAGAIN) { @@ -255,13 +175,11 @@ again: return; } - xen_backend_device_create(xenbus, type, name, opts, &local_err); + xen_backend_device_create(xenbus, type, name, opts, errp); qobject_unref(opts); - if (local_err) { - error_propagate_prepend(errp, local_err, - "failed to create '%s' device '%s': ", - type, name); + if (*errp) { + error_prepend(errp, "failed to create '%s' device '%s': ", type, name); } } @@ -273,7 +191,7 @@ static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) trace_xen_bus_type_enumerate(type); - backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); + backend = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); if (!backend) { goto out; } @@ -281,13 +199,19 @@ static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) for (i = 0; i < n; i++) { char *backend_path = g_strdup_printf("%s/%s", domain_path, backend[i]); - enum xenbus_state backend_state; + enum xenbus_state state; + unsigned int online; if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "state", - NULL, "%u", &backend_state) != 1) - backend_state = XenbusStateUnknown; + NULL, "%u", &state) != 1) + state = XenbusStateUnknown; + + if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "online", + NULL, "%u", &online) != 1) + online = 0; - if (backend_state == XenbusStateInitialising) { + if (online && state == XenbusStateInitialising && + !xen_backend_exists(type, backend[i])) { Error *local_err = NULL; xen_bus_backend_create(xenbus, type, backend[i], backend_path, @@ -306,15 +230,14 @@ out: g_free(domain_path); } -static void xen_bus_enumerate(void *opaque) +static void xen_bus_enumerate(XenBus *xenbus) { - XenBus *xenbus = opaque; char **type; unsigned int i, n; trace_xen_bus_enumerate(); - type = xs_directory(xenbus->xsh, XBT_NULL, "backend", &n); + type = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, "backend", &n); if (!type) { return; } @@ -326,57 +249,81 @@ static void xen_bus_enumerate(void *opaque) free(type); } -static void xen_bus_unrealize(BusState *bus, Error **errp) +static void xen_bus_device_cleanup(XenDevice *xendev) { - XenBus *xenbus = XEN_BUS(bus); + const char *type = object_get_typename(OBJECT(xendev)); + Error *local_err = NULL; - trace_xen_bus_unrealize(); + trace_xen_bus_device_cleanup(type, xendev->name); - if (xenbus->backend_watch) { - xen_bus_remove_watch(xenbus, xenbus->backend_watch, NULL); - xenbus->backend_watch = NULL; + g_assert(!xendev->backend_online); + + if (!xen_backend_try_device_destroy(xendev, &local_err)) { + object_unparent(OBJECT(xendev)); } - if (!xenbus->xsh) { - return; + if (local_err) { + error_report_err(local_err); } +} - qemu_set_fd_handler(xs_fileno(xenbus->xsh), NULL, NULL, NULL); +static void xen_bus_cleanup(XenBus *xenbus) +{ + XenDevice *xendev, *next; - xs_close(xenbus->xsh); + trace_xen_bus_cleanup(); + + QLIST_FOREACH_SAFE(xendev, &xenbus->inactive_devices, list, next) { + g_assert(xendev->inactive); + QLIST_REMOVE(xendev, list); + xen_bus_device_cleanup(xendev); + } } -static void xen_bus_watch(void *opaque) +static void xen_bus_backend_changed(void *opaque, const char *path) { XenBus *xenbus = opaque; - char **v; - const char *token; - g_assert(xenbus->xsh); + xen_bus_enumerate(xenbus); + xen_bus_cleanup(xenbus); +} - v = xs_check_watch(xenbus->xsh); - if (!v) { - return; - } +static void xen_bus_unrealize(BusState *bus) +{ + XenBus *xenbus = XEN_BUS(bus); - token = v[XS_WATCH_TOKEN]; + trace_xen_bus_unrealize(); - trace_xen_bus_watch(token); + if (xenbus->backend_watch) { + unsigned int i; - notifier_list_notify(&xenbus->watch_notifiers, (void *)token); + for (i = 0; i < xenbus->backend_types; i++) { + if (xenbus->backend_watch[i]) { + xs_node_unwatch(xenbus->xsh, xenbus->backend_watch[i]); + } + } + + g_free(xenbus->backend_watch); + xenbus->backend_watch = NULL; + } - free(v); + if (xenbus->xsh) { + qemu_xen_xs_close(xenbus->xsh); + } } static void xen_bus_realize(BusState *bus, Error **errp) { + char *key = g_strdup_printf("%u", xen_domid); XenBus *xenbus = XEN_BUS(bus); unsigned int domid; + const char **type; + unsigned int i; Error *local_err = NULL; trace_xen_bus_realize(); - xenbus->xsh = xs_open(0); + xenbus->xsh = qemu_xen_xs_open(); if (!xenbus->xsh) { error_setg_errno(errp, errno, "failed xs_open"); goto fail; @@ -389,25 +336,35 @@ static void xen_bus_realize(BusState *bus, Error **errp) xenbus->backend_id = 0; /* Assume lack of node means dom0 */ } - 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) { - /* This need not be treated as a hard error so don't propagate */ - error_reportf_err(local_err, - "failed to set up enumeration watch: "); + type = xen_backend_get_types(&xenbus->backend_types); + xenbus->backend_watch = g_new(struct qemu_xs_watch *, + xenbus->backend_types); + + for (i = 0; i < xenbus->backend_types; i++) { + char *node = g_strdup_printf("backend/%s", type[i]); + + xenbus->backend_watch[i] = + xs_node_watch(xenbus->xsh, node, key, xen_bus_backend_changed, + xenbus, &local_err); + if (local_err) { + /* This need not be treated as a hard error so don't propagate */ + error_reportf_err(local_err, + "failed to set up '%s' enumeration watch: ", + type[i]); + } + + g_free(node); } + g_free(type); + g_free(key); return; fail: - xen_bus_unrealize(bus, &error_abort); + xen_bus_unrealize(bus); + g_free(key); } static void xen_bus_unplug_request(HotplugHandler *hotplug, @@ -463,6 +420,7 @@ void xen_device_backend_printf(XenDevice *xendev, const char *key, } } +G_GNUC_SCANF(3, 4) static int xen_device_backend_scanf(XenDevice *xendev, const char *key, const char *fmt, ...) { @@ -515,7 +473,24 @@ static void xen_device_backend_set_online(XenDevice *xendev, bool online) xen_device_backend_printf(xendev, "online", "%u", online); } -static void xen_device_backend_changed(void *opaque) +/* + * Tell from the state whether the frontend is likely alive, + * i.e. it will react to a change of state of the backend. + */ +static bool xen_device_frontend_is_active(XenDevice *xendev) +{ + switch (xendev->frontend_state) { + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateConnected: + case XenbusStateClosing: + return true; + default: + return false; + } +} + +static void xen_device_backend_changed(void *opaque, const char *path) { XenDevice *xendev = opaque; const char *type = object_get_typename(OBJECT(xendev)); @@ -538,81 +513,68 @@ static void xen_device_backend_changed(void *opaque) /* * If the toolstack (or unplug request callback) has set the backend - * state to Closing, but there is no active frontend (i.e. the - * state is not Connected) then set the backend state to Closed. + * state to Closing, but there is no active frontend then set the + * backend state to Closed. */ - if (xendev->backend_state == XenbusStateClosing && - xendev->frontend_state != XenbusStateConnected) { + if (state == XenbusStateClosing && + !xen_device_frontend_is_active(xendev)) { xen_device_backend_set_state(xendev, XenbusStateClosed); } /* - * 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 is not 'online' then the device should be destroyed. + * If a backend is still 'online' then we should leave it alone but, + * if a backend is not 'online', then the device is a candidate + * for destruction. Hence add it to the 'inactive' list to be cleaned + * by xen_bus_cleanup(). */ - 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)) { - Error *local_err = NULL; + if (!online && + (state == XenbusStateClosed || state == XenbusStateInitialising || + state == XenbusStateInitWait || state == XenbusStateUnknown) && + !xendev->inactive) { + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - if (!xen_backend_try_device_destroy(xendev, &local_err)) { - object_unparent(OBJECT(xendev)); - } + xendev->inactive = true; + QLIST_INSERT_HEAD(&xenbus->inactive_devices, xendev, list); - if (local_err) { - error_report_err(local_err); - } + /* + * Re-write the state to cause a XenBus backend_watch notification, + * resulting in a call to xen_bus_cleanup(). + */ + xen_device_backend_printf(xendev, "state", "%u", state); } } static void xen_device_backend_create(XenDevice *xendev, Error **errp) { + ERRP_GUARD(); XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - struct xs_permissions perms[2]; - Error *local_err = NULL; xendev->backend_path = xen_device_get_backend_path(xendev); - perms[0].id = xenbus->backend_id; - perms[0].perms = XS_PERM_NONE; - perms[1].id = xendev->frontend_id; - perms[1].perms = XS_PERM_READ; - g_assert(xenbus->xsh); - xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, perms, - ARRAY_SIZE(perms), &local_err); - if (local_err) { - error_propagate_prepend(errp, local_err, - "failed to create backend: "); + xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, + xenbus->backend_id, xendev->frontend_id, XS_PERM_READ, errp); + if (*errp) { + error_prepend(errp, "failed to create backend: "); return; } 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_prepend(errp, local_err, - "failed to watch backend state: "); + xs_node_watch(xendev->xsh, xendev->backend_path, + "state", xen_device_backend_changed, xendev, + errp); + if (*errp) { + error_prepend(errp, "failed to watch backend state: "); return; } 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_prepend(errp, local_err, - "failed to watch backend online: "); + xs_node_watch(xendev->xsh, xendev->backend_path, + "online", xen_device_backend_changed, xendev, + errp); + if (*errp) { + error_prepend(errp, "failed to watch backend online: "); return; } } @@ -623,12 +585,12 @@ static void xen_device_backend_destroy(XenDevice *xendev) Error *local_err = NULL; if (xendev->backend_online_watch) { - xen_bus_remove_watch(xenbus, xendev->backend_online_watch, NULL); + xs_node_unwatch(xendev->xsh, xendev->backend_online_watch); xendev->backend_online_watch = NULL; } if (xendev->backend_state_watch) { - xen_bus_remove_watch(xenbus, xendev->backend_state_watch, NULL); + xs_node_unwatch(xendev->xsh, xendev->backend_state_watch); xendev->backend_state_watch = NULL; } @@ -685,7 +647,8 @@ int xen_device_frontend_scanf(XenDevice *xendev, const char *key, } static void xen_device_frontend_set_state(XenDevice *xendev, - enum xenbus_state state) + enum xenbus_state state, + bool publish) { const char *type = object_get_typename(OBJECT(xendev)); @@ -697,10 +660,12 @@ static void xen_device_frontend_set_state(XenDevice *xendev, xs_strstate(state)); xendev->frontend_state = state; - xen_device_frontend_printf(xendev, "state", "%u", state); + if (publish) { + xen_device_frontend_printf(xendev, "state", "%u", state); + } } -static void xen_device_frontend_changed(void *opaque) +static void xen_device_frontend_changed(void *opaque, const char *path) { XenDevice *xendev = opaque; XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); @@ -713,7 +678,18 @@ static void xen_device_frontend_changed(void *opaque) state = XenbusStateUnknown; } - xen_device_frontend_set_state(xendev, state); + xen_device_frontend_set_state(xendev, state, false); + + if (state == XenbusStateInitialising && + xendev->backend_state == XenbusStateClosed && + xendev->backend_online) { + /* + * The frontend is re-initializing so switch back to + * InitWait. + */ + xen_device_backend_set_state(xendev, XenbusStateInitWait); + return; + } if (xendev_class->frontend_changed) { Error *local_err = NULL; @@ -726,35 +702,50 @@ static void xen_device_frontend_changed(void *opaque) } } -static void xen_device_frontend_create(XenDevice *xendev, Error **errp) +static bool xen_device_frontend_exists(XenDevice *xendev) { - XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); - struct xs_permissions perms[2]; - Error *local_err = NULL; + enum xenbus_state state; - xendev->frontend_path = xen_device_get_frontend_path(xendev); + return (xen_device_frontend_scanf(xendev, "state", "%u", &state) == 1); +} - perms[0].id = xendev->frontend_id; - perms[0].perms = XS_PERM_NONE; - perms[1].id = xenbus->backend_id; - perms[1].perms = XS_PERM_READ | XS_PERM_WRITE; +static void xen_device_frontend_create(XenDevice *xendev, Error **errp) +{ + ERRP_GUARD(); + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); - g_assert(xenbus->xsh); + if (xendev_class->get_frontend_path) { + xendev->frontend_path = xendev_class->get_frontend_path(xendev, errp); + if (!xendev->frontend_path) { + error_prepend(errp, "failed to create frontend: "); + return; + } + } else { + xendev->frontend_path = xen_device_get_frontend_path(xendev); + } - xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, perms, - ARRAY_SIZE(perms), &local_err); - if (local_err) { - error_propagate_prepend(errp, local_err, - "failed to create frontend: "); - return; + /* + * The frontend area may have already been created by a legacy + * toolstack. + */ + if (!xen_device_frontend_exists(xendev)) { + g_assert(xenbus->xsh); + + xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, + xendev->frontend_id, xenbus->backend_id, + XS_PERM_READ | XS_PERM_WRITE, errp); + if (*errp) { + error_prepend(errp, "failed to create frontend: "); + return; + } } xendev->frontend_state_watch = - xen_bus_add_watch(xenbus, xendev->frontend_path, "state", - xen_device_frontend_changed, xendev, &local_err); - if (local_err) { - error_propagate_prepend(errp, local_err, - "failed to watch frontend state: "); + xs_node_watch(xendev->xsh, xendev->frontend_path, "state", + xen_device_frontend_changed, xendev, errp); + if (*errp) { + error_prepend(errp, "failed to watch frontend state: "); } } @@ -764,7 +755,7 @@ static void xen_device_frontend_destroy(XenDevice *xendev) Error *local_err = NULL; if (xendev->frontend_state_watch) { - xen_bus_remove_watch(xenbus, xendev->frontend_state_watch, NULL); + xs_node_unwatch(xendev->xsh, xendev->frontend_state_watch); xendev->frontend_state_watch = NULL; } @@ -787,7 +778,7 @@ static void xen_device_frontend_destroy(XenDevice *xendev) void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, Error **errp) { - if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) { + if (qemu_xen_gnttab_set_max_grants(xendev->xgth, nr_refs)) { error_setg_errno(errp, errno, "xengnttab_set_max_grants failed"); } } @@ -796,9 +787,8 @@ void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, unsigned int nr_refs, int prot, Error **errp) { - void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs, - xendev->frontend_id, refs, - prot); + void *map = qemu_xen_gnttab_map_refs(xendev->xgth, nr_refs, + xendev->frontend_id, refs, prot); if (!map) { error_setg_errno(errp, errno, @@ -808,128 +798,69 @@ void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, return map; } -void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, +void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, uint32_t *refs, unsigned int nr_refs, Error **errp) { - if (xengnttab_unmap(xendev->xgth, map, nr_refs)) { + if (qemu_xen_gnttab_unmap(xendev->xgth, map, refs, nr_refs)) { error_setg_errno(errp, errno, "xengnttab_unmap failed"); } } -static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain, - XenDeviceGrantCopySegment segs[], - unsigned int nr_segs, Error **errp) -{ - uint32_t *refs = g_new(uint32_t, nr_segs); - int prot = to_domain ? PROT_WRITE : PROT_READ; - void *map; - unsigned int i; - - for (i = 0; i < nr_segs; i++) { - XenDeviceGrantCopySegment *seg = &segs[i]; - - refs[i] = to_domain ? seg->dest.foreign.ref : - seg->source.foreign.ref; - } - - map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs, - xendev->frontend_id, refs, - prot); - if (!map) { - error_setg_errno(errp, errno, - "xengnttab_map_domain_grant_refs failed"); - goto done; - } - - for (i = 0; i < nr_segs; i++) { - XenDeviceGrantCopySegment *seg = &segs[i]; - void *page = map + (i * XC_PAGE_SIZE); - - if (to_domain) { - memcpy(page + seg->dest.foreign.offset, seg->source.virt, - seg->len); - } else { - memcpy(seg->dest.virt, page + seg->source.foreign.offset, - seg->len); - } - } - - if (xengnttab_unmap(xendev->xgth, map, nr_segs)) { - error_setg_errno(errp, errno, "xengnttab_unmap failed"); - } - -done: - g_free(refs); -} - void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, XenDeviceGrantCopySegment segs[], unsigned int nr_segs, Error **errp) { - xengnttab_grant_copy_segment_t *xengnttab_segs; - unsigned int i; - - if (!xendev->feature_grant_copy) { - compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp); - return; - } - - xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); - - for (i = 0; i < nr_segs; i++) { - XenDeviceGrantCopySegment *seg = &segs[i]; - xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; - - if (to_domain) { - xengnttab_seg->flags = GNTCOPY_dest_gref; - xengnttab_seg->dest.foreign.domid = xendev->frontend_id; - xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; - xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; - xengnttab_seg->source.virt = seg->source.virt; - } else { - xengnttab_seg->flags = GNTCOPY_source_gref; - xengnttab_seg->source.foreign.domid = xendev->frontend_id; - xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; - xengnttab_seg->source.foreign.offset = - seg->source.foreign.offset; - xengnttab_seg->dest.virt = seg->dest.virt; - } - - xengnttab_seg->len = seg->len; - } - - if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) { - error_setg_errno(errp, errno, "xengnttab_grant_copy failed"); - goto done; - } - - for (i = 0; i < nr_segs; i++) { - xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; - - if (xengnttab_seg->status != GNTST_okay) { - error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i); - break; - } - } - -done: - g_free(xengnttab_segs); + qemu_xen_gnttab_grant_copy(xendev->xgth, to_domain, xendev->frontend_id, + (XenGrantCopySegment *)segs, nr_segs, errp); } struct XenEventChannel { + QLIST_ENTRY(XenEventChannel) list; + AioContext *ctx; + xenevtchn_handle *xeh; evtchn_port_t local_port; XenEventHandler handler; void *opaque; - Notifier notifier; }; -static void event_notify(Notifier *n, void *data) +static bool xen_device_poll(void *opaque) { - XenEventChannel *channel = container_of(n, XenEventChannel, notifier); - unsigned long port = (unsigned long)data; + XenEventChannel *channel = opaque; + + return channel->handler(channel->opaque); +} + +static void xen_device_event(void *opaque) +{ + XenEventChannel *channel = opaque; + unsigned long port = qemu_xen_evtchn_pending(channel->xeh); if (port == channel->local_port) { - channel->handler(channel->opaque); + xen_device_poll(channel); + + qemu_xen_evtchn_unmask(channel->xeh, port); + } +} + +void xen_device_set_event_channel_context(XenDevice *xendev, + XenEventChannel *channel, + AioContext *ctx, + Error **errp) +{ + if (!channel) { + error_setg(errp, "bad channel"); + return; + } + + if (channel->ctx) + aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), + NULL, NULL, NULL, NULL, NULL); + + channel->ctx = ctx; + if (ctx) { + aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), + xen_device_event, NULL, xen_device_poll, NULL, + channel); } } @@ -941,24 +872,41 @@ XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, XenEventChannel *channel = g_new0(XenEventChannel, 1); xenevtchn_port_or_error_t local_port; - local_port = xenevtchn_bind_interdomain(xendev->xeh, + channel->xeh = qemu_xen_evtchn_open(); + if (!channel->xeh) { + error_setg_errno(errp, errno, "failed xenevtchn_open"); + goto fail; + } + + local_port = qemu_xen_evtchn_bind_interdomain(channel->xeh, xendev->frontend_id, port); if (local_port < 0) { error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed"); - - g_free(channel); - return NULL; + goto fail; } channel->local_port = local_port; channel->handler = handler; channel->opaque = opaque; - channel->notifier.notify = event_notify; - notifier_list_add(&xendev->event_notifiers, &channel->notifier); + /* Only reason for failure is a NULL channel */ + xen_device_set_event_channel_context(xendev, channel, + qemu_get_aio_context(), + &error_abort); + + QLIST_INSERT_HEAD(&xendev->event_channels, channel, list); return channel; + +fail: + if (channel->xeh) { + qemu_xen_evtchn_close(channel->xeh); + } + + g_free(channel); + + return NULL; } void xen_device_notify_event_channel(XenDevice *xendev, @@ -970,11 +918,16 @@ void xen_device_notify_event_channel(XenDevice *xendev, return; } - if (xenevtchn_notify(xendev->xeh, channel->local_port) < 0) { + if (qemu_xen_evtchn_notify(channel->xeh, channel->local_port) < 0) { error_setg_errno(errp, errno, "xenevtchn_notify failed"); } } +unsigned int xen_event_channel_get_local_port(XenEventChannel *channel) +{ + return channel->local_port; +} + void xen_device_unbind_event_channel(XenDevice *xendev, XenEventChannel *channel, Error **errp) @@ -984,20 +937,27 @@ void xen_device_unbind_event_channel(XenDevice *xendev, return; } - notifier_remove(&channel->notifier); + QLIST_REMOVE(channel, list); + + if (channel->ctx) { + aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), + NULL, NULL, NULL, NULL, NULL); + } - if (xenevtchn_unbind(xendev->xeh, channel->local_port) < 0) { + if (qemu_xen_evtchn_unbind(channel->xeh, channel->local_port) < 0) { error_setg_errno(errp, errno, "xenevtchn_unbind failed"); } + qemu_xen_evtchn_close(channel->xeh); g_free(channel); } -static void xen_device_unrealize(DeviceState *dev, Error **errp) +static void xen_device_unrealize(DeviceState *dev) { XenDevice *xendev = XEN_DEVICE(dev); XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); const char *type = object_get_typename(OBJECT(xendev)); + XenEventChannel *channel, *next; if (!xendev->name) { return; @@ -1011,23 +971,27 @@ static void xen_device_unrealize(DeviceState *dev, Error **errp) } if (xendev_class->unrealize) { - xendev_class->unrealize(xendev, errp); + xendev_class->unrealize(xendev); + } + + /* Make sure all event channels are cleaned up */ + QLIST_FOREACH_SAFE(channel, &xendev->event_channels, list, next) { + xen_device_unbind_event_channel(xendev, channel, NULL); } xen_device_frontend_destroy(xendev); xen_device_backend_destroy(xendev); - if (xendev->xeh) { - qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), NULL, NULL, NULL); - xenevtchn_close(xendev->xeh); - xendev->xeh = NULL; - } - if (xendev->xgth) { - xengnttab_close(xendev->xgth); + qemu_xen_gnttab_close(xendev->xgth); xendev->xgth = NULL; } + if (xendev->xsh) { + qemu_xen_xs_close(xendev->xsh); + xendev->xsh = NULL; + } + g_free(xendev->name); xendev->name = NULL; } @@ -1036,26 +1000,16 @@ static void xen_device_exit(Notifier *n, void *data) { XenDevice *xendev = container_of(n, XenDevice, exit); - xen_device_unrealize(DEVICE(xendev), &error_abort); -} - -static void xen_device_event(void *opaque) -{ - XenDevice *xendev = opaque; - unsigned long port = xenevtchn_pending(xendev->xeh); - - notifier_list_notify(&xendev->event_notifiers, (void *)port); - - xenevtchn_unmask(xendev->xeh, port); + xen_device_unrealize(DEVICE(xendev)); } static void xen_device_realize(DeviceState *dev, Error **errp) { + ERRP_GUARD(); XenDevice *xendev = XEN_DEVICE(dev); XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); const char *type = object_get_typename(OBJECT(xendev)); - Error *local_err = NULL; if (xendev->frontend_id == DOMID_INVALID) { xendev->frontend_id = xen_domid; @@ -1071,54 +1025,36 @@ static void xen_device_realize(DeviceState *dev, Error **errp) goto unrealize; } - xendev->name = xendev_class->get_name(xendev, &local_err); - if (local_err) { - error_propagate_prepend(errp, local_err, - "failed to get device name: "); + xendev->name = xendev_class->get_name(xendev, errp); + if (*errp) { + error_prepend(errp, "failed to get device name: "); goto unrealize; } trace_xen_device_realize(type, xendev->name); - xendev->xgth = xengnttab_open(NULL, 0); - if (!xendev->xgth) { - error_setg_errno(errp, errno, "failed xengnttab_open"); + xendev->xsh = qemu_xen_xs_open(); + if (!xendev->xsh) { + error_setg_errno(errp, errno, "failed xs_open"); goto unrealize; } - xendev->feature_grant_copy = - (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0); - - xendev->xeh = xenevtchn_open(NULL, 0); - if (!xendev->xeh) { - error_setg_errno(errp, errno, "failed xenevtchn_open"); + xendev->xgth = qemu_xen_gnttab_open(); + if (!xendev->xgth) { + error_setg_errno(errp, errno, "failed xengnttab_open"); goto unrealize; } - notifier_list_init(&xendev->event_notifiers); - qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), xen_device_event, NULL, - xendev); - - xen_device_backend_create(xendev, &local_err); - if (local_err) { - error_propagate(errp, local_err); + xen_device_backend_create(xendev, errp); + if (*errp) { goto unrealize; } - xen_device_frontend_create(xendev, &local_err); - if (local_err) { - error_propagate(errp, local_err); + xen_device_frontend_create(xendev, errp); + if (*errp) { goto unrealize; } - if (xendev_class->realize) { - xendev_class->realize(xendev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto unrealize; - } - } - xen_device_backend_printf(xendev, "frontend", "%s", xendev->frontend_path); xen_device_backend_printf(xendev, "frontend-id", "%u", @@ -1128,19 +1064,28 @@ static void xen_device_realize(DeviceState *dev, Error **errp) xen_device_backend_set_online(xendev, true); xen_device_backend_set_state(xendev, XenbusStateInitWait); - xen_device_frontend_printf(xendev, "backend", "%s", - xendev->backend_path); - xen_device_frontend_printf(xendev, "backend-id", "%u", - xenbus->backend_id); + if (!xen_device_frontend_exists(xendev)) { + xen_device_frontend_printf(xendev, "backend", "%s", + xendev->backend_path); + xen_device_frontend_printf(xendev, "backend-id", "%u", + xenbus->backend_id); - xen_device_frontend_set_state(xendev, XenbusStateInitialising); + xen_device_frontend_set_state(xendev, XenbusStateInitialising, true); + } + + if (xendev_class->realize) { + xendev_class->realize(xendev, errp); + if (*errp) { + goto unrealize; + } + } xendev->exit.notify = xen_device_exit; qemu_add_exit_notifier(&xendev->exit); return; unrealize: - xen_device_unrealize(dev, &error_abort); + xen_device_unrealize(dev); } static Property xen_device_props[] = { @@ -1155,7 +1100,7 @@ static void xen_device_class_init(ObjectClass *class, void *data) dev_class->realize = xen_device_realize; dev_class->unrealize = xen_device_unrealize; - dev_class->props = xen_device_props; + device_class_set_props(dev_class, xen_device_props); dev_class->bus_type = TYPE_XEN_BUS; } @@ -1191,9 +1136,12 @@ type_init(xen_register_types) void xen_bus_init(void) { - DeviceState *dev = qdev_create(NULL, TYPE_XEN_BRIDGE); - BusState *bus = qbus_create(TYPE_XEN_BUS, dev, NULL); + DeviceState *dev = qdev_new(TYPE_XEN_BRIDGE); + BusState *bus = qbus_new(TYPE_XEN_BUS, dev, NULL); + + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + qbus_set_bus_hotplug_handler(bus); - qdev_init_nofail(dev); - qbus_set_bus_hotplug_handler(bus, &error_abort); + qemu_create_nic_bus_devices(bus, TYPE_XEN_DEVICE, "xen-net-device", + "xen", "xen-net-device"); } |