1108f7bbaSPaul Durrant /* 2108f7bbaSPaul Durrant * Copyright (c) 2018 Citrix Systems Inc. 3108f7bbaSPaul Durrant * 4108f7bbaSPaul Durrant * This work is licensed under the terms of the GNU GPL, version 2 or later. 5108f7bbaSPaul Durrant * See the COPYING file in the top-level directory. 6108f7bbaSPaul Durrant */ 7108f7bbaSPaul Durrant 8108f7bbaSPaul Durrant #include "qemu/osdep.h" 982a29e30SPaul Durrant #include "qemu/main-loop.h" 100b8fa32fSMarkus Armbruster #include "qemu/module.h" 1182a29e30SPaul Durrant #include "qemu/uuid.h" 12a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 13108f7bbaSPaul Durrant #include "hw/sysbus.h" 14094a2239SPaul Durrant #include "hw/xen/xen.h" 15a783f8adSPaul Durrant #include "hw/xen/xen-backend.h" 16108f7bbaSPaul Durrant #include "hw/xen/xen-bus.h" 17094a2239SPaul Durrant #include "hw/xen/xen-bus-helper.h" 18094a2239SPaul Durrant #include "monitor/monitor.h" 19108f7bbaSPaul Durrant #include "qapi/error.h" 20a783f8adSPaul Durrant #include "qapi/qmp/qdict.h" 21094a2239SPaul Durrant #include "sysemu/sysemu.h" 22108f7bbaSPaul Durrant #include "trace.h" 23108f7bbaSPaul Durrant 24094a2239SPaul Durrant static char *xen_device_get_backend_path(XenDevice *xendev) 25094a2239SPaul Durrant { 26094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 27094a2239SPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 28094a2239SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 29094a2239SPaul Durrant const char *backend = xendev_class->backend; 30094a2239SPaul Durrant 31094a2239SPaul Durrant if (!backend) { 32094a2239SPaul Durrant backend = type; 33094a2239SPaul Durrant } 34094a2239SPaul Durrant 35094a2239SPaul Durrant return g_strdup_printf("/local/domain/%u/backend/%s/%u/%s", 36094a2239SPaul Durrant xenbus->backend_id, backend, xendev->frontend_id, 37094a2239SPaul Durrant xendev->name); 38094a2239SPaul Durrant } 39094a2239SPaul Durrant 40094a2239SPaul Durrant static char *xen_device_get_frontend_path(XenDevice *xendev) 41094a2239SPaul Durrant { 42094a2239SPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 43094a2239SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 44094a2239SPaul Durrant const char *device = xendev_class->device; 45094a2239SPaul Durrant 46094a2239SPaul Durrant if (!device) { 47094a2239SPaul Durrant device = type; 48094a2239SPaul Durrant } 49094a2239SPaul Durrant 50094a2239SPaul Durrant return g_strdup_printf("/local/domain/%u/device/%s/%s", 51094a2239SPaul Durrant xendev->frontend_id, device, xendev->name); 52094a2239SPaul Durrant } 53094a2239SPaul Durrant 54b6af8926SPaul Durrant static void xen_device_unplug(XenDevice *xendev, Error **errp) 55b6af8926SPaul Durrant { 561de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 57b6af8926SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 58b6af8926SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 59b6af8926SPaul Durrant xs_transaction_t tid; 60b6af8926SPaul Durrant 61b6af8926SPaul Durrant trace_xen_device_unplug(type, xendev->name); 62b6af8926SPaul Durrant 63b6af8926SPaul Durrant /* Mimic the way the Xen toolstack does an unplug */ 64b6af8926SPaul Durrant again: 65ba2a92dbSPaul Durrant tid = qemu_xen_xs_transaction_start(xenbus->xsh); 66b6af8926SPaul Durrant if (tid == XBT_NULL) { 67b6af8926SPaul Durrant error_setg_errno(errp, errno, "failed xs_transaction_start"); 68b6af8926SPaul Durrant return; 69b6af8926SPaul Durrant } 70b6af8926SPaul Durrant 71b6af8926SPaul Durrant xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "online", 721de7096dSVladimir Sementsov-Ogievskiy errp, "%u", 0); 731de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 74b6af8926SPaul Durrant goto abort; 75b6af8926SPaul Durrant } 76b6af8926SPaul Durrant 77b6af8926SPaul Durrant xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "state", 781de7096dSVladimir Sementsov-Ogievskiy errp, "%u", XenbusStateClosing); 791de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 80b6af8926SPaul Durrant goto abort; 81b6af8926SPaul Durrant } 82b6af8926SPaul Durrant 83ba2a92dbSPaul Durrant if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { 84b6af8926SPaul Durrant if (errno == EAGAIN) { 85b6af8926SPaul Durrant goto again; 86b6af8926SPaul Durrant } 87b6af8926SPaul Durrant 88b6af8926SPaul Durrant error_setg_errno(errp, errno, "failed xs_transaction_end"); 89b6af8926SPaul Durrant } 90b6af8926SPaul Durrant 91b6af8926SPaul Durrant return; 92b6af8926SPaul Durrant 93b6af8926SPaul Durrant abort: 94b6af8926SPaul Durrant /* 95b6af8926SPaul Durrant * We only abort if there is already a failure so ignore any error 96b6af8926SPaul Durrant * from ending the transaction. 97b6af8926SPaul Durrant */ 98ba2a92dbSPaul Durrant qemu_xen_xs_transaction_end(xenbus->xsh, tid, true); 99b6af8926SPaul Durrant } 100b6af8926SPaul Durrant 101094a2239SPaul Durrant static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent) 102094a2239SPaul Durrant { 103094a2239SPaul Durrant XenDevice *xendev = XEN_DEVICE(dev); 104094a2239SPaul Durrant 105094a2239SPaul Durrant monitor_printf(mon, "%*sname = '%s' frontend_id = %u\n", 106094a2239SPaul Durrant indent, "", xendev->name, xendev->frontend_id); 107094a2239SPaul Durrant } 108094a2239SPaul Durrant 109094a2239SPaul Durrant static char *xen_bus_get_dev_path(DeviceState *dev) 110094a2239SPaul Durrant { 111094a2239SPaul Durrant return xen_device_get_backend_path(XEN_DEVICE(dev)); 112094a2239SPaul Durrant } 113094a2239SPaul Durrant 114a783f8adSPaul Durrant static void xen_bus_backend_create(XenBus *xenbus, const char *type, 115a783f8adSPaul Durrant const char *name, char *path, 116a783f8adSPaul Durrant Error **errp) 117a783f8adSPaul Durrant { 1181de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 119a783f8adSPaul Durrant xs_transaction_t tid; 120a783f8adSPaul Durrant char **key; 121a783f8adSPaul Durrant QDict *opts; 122a783f8adSPaul Durrant unsigned int i, n; 123a783f8adSPaul Durrant 124a783f8adSPaul Durrant trace_xen_bus_backend_create(type, path); 125a783f8adSPaul Durrant 126a783f8adSPaul Durrant again: 127ba2a92dbSPaul Durrant tid = qemu_xen_xs_transaction_start(xenbus->xsh); 128a783f8adSPaul Durrant if (tid == XBT_NULL) { 129a783f8adSPaul Durrant error_setg(errp, "failed xs_transaction_start"); 130a783f8adSPaul Durrant return; 131a783f8adSPaul Durrant } 132a783f8adSPaul Durrant 133ba2a92dbSPaul Durrant key = qemu_xen_xs_directory(xenbus->xsh, tid, path, &n); 134a783f8adSPaul Durrant if (!key) { 135ba2a92dbSPaul Durrant if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, true)) { 136a783f8adSPaul Durrant error_setg_errno(errp, errno, "failed xs_transaction_end"); 137a783f8adSPaul Durrant } 138a783f8adSPaul Durrant return; 139a783f8adSPaul Durrant } 140a783f8adSPaul Durrant 141a783f8adSPaul Durrant opts = qdict_new(); 142a783f8adSPaul Durrant for (i = 0; i < n; i++) { 143a783f8adSPaul Durrant char *val; 144a783f8adSPaul Durrant 145a783f8adSPaul Durrant /* 146a783f8adSPaul Durrant * Assume anything found in the xenstore backend area, other than 147a783f8adSPaul Durrant * the keys created for a generic XenDevice, are parameters 148a783f8adSPaul Durrant * to be used to configure the backend. 149a783f8adSPaul Durrant */ 150a783f8adSPaul Durrant if (!strcmp(key[i], "state") || 151a783f8adSPaul Durrant !strcmp(key[i], "online") || 152a783f8adSPaul Durrant !strcmp(key[i], "frontend") || 153a783f8adSPaul Durrant !strcmp(key[i], "frontend-id") || 154a783f8adSPaul Durrant !strcmp(key[i], "hotplug-status")) 155a783f8adSPaul Durrant continue; 156a783f8adSPaul Durrant 157a783f8adSPaul Durrant if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms", 158a783f8adSPaul Durrant &val) == 1) { 159a783f8adSPaul Durrant qdict_put_str(opts, key[i], val); 160a783f8adSPaul Durrant free(val); 161a783f8adSPaul Durrant } 162a783f8adSPaul Durrant } 163a783f8adSPaul Durrant 164a783f8adSPaul Durrant free(key); 165a783f8adSPaul Durrant 166ba2a92dbSPaul Durrant if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { 167a783f8adSPaul Durrant qobject_unref(opts); 168a783f8adSPaul Durrant 169a783f8adSPaul Durrant if (errno == EAGAIN) { 170a783f8adSPaul Durrant goto again; 171a783f8adSPaul Durrant } 172a783f8adSPaul Durrant 173a783f8adSPaul Durrant error_setg_errno(errp, errno, "failed xs_transaction_end"); 174a783f8adSPaul Durrant return; 175a783f8adSPaul Durrant } 176a783f8adSPaul Durrant 1771de7096dSVladimir Sementsov-Ogievskiy xen_backend_device_create(xenbus, type, name, opts, errp); 178a783f8adSPaul Durrant qobject_unref(opts); 179a783f8adSPaul Durrant 1801de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 1811de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to create '%s' device '%s': ", type, name); 182a783f8adSPaul Durrant } 183a783f8adSPaul Durrant } 184a783f8adSPaul Durrant 185a783f8adSPaul Durrant static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) 186a783f8adSPaul Durrant { 187a783f8adSPaul Durrant char *domain_path = g_strdup_printf("backend/%s/%u", type, xen_domid); 188a783f8adSPaul Durrant char **backend; 189a783f8adSPaul Durrant unsigned int i, n; 190a783f8adSPaul Durrant 191a783f8adSPaul Durrant trace_xen_bus_type_enumerate(type); 192a783f8adSPaul Durrant 193ba2a92dbSPaul Durrant backend = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); 194a783f8adSPaul Durrant if (!backend) { 195a783f8adSPaul Durrant goto out; 196a783f8adSPaul Durrant } 197a783f8adSPaul Durrant 198a783f8adSPaul Durrant for (i = 0; i < n; i++) { 199a783f8adSPaul Durrant char *backend_path = g_strdup_printf("%s/%s", domain_path, 200a783f8adSPaul Durrant backend[i]); 2013809f758SPaul Durrant enum xenbus_state state; 2023809f758SPaul Durrant unsigned int online; 203a783f8adSPaul Durrant 204a783f8adSPaul Durrant if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "state", 2053809f758SPaul Durrant NULL, "%u", &state) != 1) 2063809f758SPaul Durrant state = XenbusStateUnknown; 207a783f8adSPaul Durrant 2083809f758SPaul Durrant if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "online", 2093809f758SPaul Durrant NULL, "%u", &online) != 1) 2103809f758SPaul Durrant online = 0; 2113809f758SPaul Durrant 212*eb6ae7a6SDavid Woodhouse if (online && state == XenbusStateInitialising && 213*eb6ae7a6SDavid Woodhouse !xen_backend_exists(type, backend[i])) { 214a783f8adSPaul Durrant Error *local_err = NULL; 215a783f8adSPaul Durrant 216a783f8adSPaul Durrant xen_bus_backend_create(xenbus, type, backend[i], backend_path, 217a783f8adSPaul Durrant &local_err); 218a783f8adSPaul Durrant if (local_err) { 219a783f8adSPaul Durrant error_report_err(local_err); 220a783f8adSPaul Durrant } 221a783f8adSPaul Durrant } 222a783f8adSPaul Durrant 223a783f8adSPaul Durrant g_free(backend_path); 224a783f8adSPaul Durrant } 225a783f8adSPaul Durrant 226a783f8adSPaul Durrant free(backend); 227a783f8adSPaul Durrant 228a783f8adSPaul Durrant out: 229a783f8adSPaul Durrant g_free(domain_path); 230a783f8adSPaul Durrant } 231a783f8adSPaul Durrant 2323809f758SPaul Durrant static void xen_bus_enumerate(XenBus *xenbus) 233a783f8adSPaul Durrant { 234a783f8adSPaul Durrant char **type; 235a783f8adSPaul Durrant unsigned int i, n; 236a783f8adSPaul Durrant 237a783f8adSPaul Durrant trace_xen_bus_enumerate(); 238a783f8adSPaul Durrant 239ba2a92dbSPaul Durrant type = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, "backend", &n); 240a783f8adSPaul Durrant if (!type) { 241a783f8adSPaul Durrant return; 242a783f8adSPaul Durrant } 243a783f8adSPaul Durrant 244a783f8adSPaul Durrant for (i = 0; i < n; i++) { 245a783f8adSPaul Durrant xen_bus_type_enumerate(xenbus, type[i]); 246a783f8adSPaul Durrant } 247a783f8adSPaul Durrant 248a783f8adSPaul Durrant free(type); 249a783f8adSPaul Durrant } 250a783f8adSPaul Durrant 2513809f758SPaul Durrant static void xen_bus_device_cleanup(XenDevice *xendev) 2523809f758SPaul Durrant { 2533809f758SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 2543809f758SPaul Durrant Error *local_err = NULL; 2553809f758SPaul Durrant 2563809f758SPaul Durrant trace_xen_bus_device_cleanup(type, xendev->name); 2573809f758SPaul Durrant 2583809f758SPaul Durrant g_assert(!xendev->backend_online); 2593809f758SPaul Durrant 2603809f758SPaul Durrant if (!xen_backend_try_device_destroy(xendev, &local_err)) { 2613809f758SPaul Durrant object_unparent(OBJECT(xendev)); 2623809f758SPaul Durrant } 2633809f758SPaul Durrant 2643809f758SPaul Durrant if (local_err) { 2653809f758SPaul Durrant error_report_err(local_err); 2663809f758SPaul Durrant } 2673809f758SPaul Durrant } 2683809f758SPaul Durrant 2693809f758SPaul Durrant static void xen_bus_cleanup(XenBus *xenbus) 2703809f758SPaul Durrant { 2713809f758SPaul Durrant XenDevice *xendev, *next; 2723809f758SPaul Durrant 2733809f758SPaul Durrant trace_xen_bus_cleanup(); 2743809f758SPaul Durrant 2753809f758SPaul Durrant QLIST_FOREACH_SAFE(xendev, &xenbus->inactive_devices, list, next) { 2763809f758SPaul Durrant g_assert(xendev->inactive); 2773809f758SPaul Durrant QLIST_REMOVE(xendev, list); 2783809f758SPaul Durrant xen_bus_device_cleanup(xendev); 2793809f758SPaul Durrant } 2803809f758SPaul Durrant } 2813809f758SPaul Durrant 282ba2a92dbSPaul Durrant static void xen_bus_backend_changed(void *opaque, const char *path) 2833809f758SPaul Durrant { 2843809f758SPaul Durrant XenBus *xenbus = opaque; 2853809f758SPaul Durrant 2863809f758SPaul Durrant xen_bus_enumerate(xenbus); 2873809f758SPaul Durrant xen_bus_cleanup(xenbus); 2883809f758SPaul Durrant } 2893809f758SPaul Durrant 290b69c3c21SMarkus Armbruster static void xen_bus_unrealize(BusState *bus) 291108f7bbaSPaul Durrant { 292094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(bus); 293094a2239SPaul Durrant 294108f7bbaSPaul Durrant trace_xen_bus_unrealize(); 295094a2239SPaul Durrant 296a783f8adSPaul Durrant if (xenbus->backend_watch) { 297c4583c8cSPaul Durrant unsigned int i; 298c4583c8cSPaul Durrant 299c4583c8cSPaul Durrant for (i = 0; i < xenbus->backend_types; i++) { 300c4583c8cSPaul Durrant if (xenbus->backend_watch[i]) { 301ba2a92dbSPaul Durrant xs_node_unwatch(xenbus->xsh, xenbus->backend_watch[i]); 302c4583c8cSPaul Durrant } 303c4583c8cSPaul Durrant } 304c4583c8cSPaul Durrant 305c4583c8cSPaul Durrant g_free(xenbus->backend_watch); 306a783f8adSPaul Durrant xenbus->backend_watch = NULL; 307a783f8adSPaul Durrant } 308a783f8adSPaul Durrant 309374752a2SPaul Durrant if (xenbus->xsh) { 310ba2a92dbSPaul Durrant qemu_xen_xs_close(xenbus->xsh); 311108f7bbaSPaul Durrant } 31282a29e30SPaul Durrant } 31382a29e30SPaul Durrant 314108f7bbaSPaul Durrant static void xen_bus_realize(BusState *bus, Error **errp) 315108f7bbaSPaul Durrant { 316c4583c8cSPaul Durrant char *key = g_strdup_printf("%u", xen_domid); 317094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(bus); 318094a2239SPaul Durrant unsigned int domid; 319c4583c8cSPaul Durrant const char **type; 320c4583c8cSPaul Durrant unsigned int i; 321a783f8adSPaul Durrant Error *local_err = NULL; 322094a2239SPaul Durrant 323108f7bbaSPaul Durrant trace_xen_bus_realize(); 324094a2239SPaul Durrant 325ba2a92dbSPaul Durrant xenbus->xsh = qemu_xen_xs_open(); 326094a2239SPaul Durrant if (!xenbus->xsh) { 327094a2239SPaul Durrant error_setg_errno(errp, errno, "failed xs_open"); 328094a2239SPaul Durrant goto fail; 329094a2239SPaul Durrant } 330094a2239SPaul Durrant 331094a2239SPaul Durrant if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */ 332094a2239SPaul Durrant "domid", NULL, "%u", &domid) == 1) { 333094a2239SPaul Durrant xenbus->backend_id = domid; 334094a2239SPaul Durrant } else { 335094a2239SPaul Durrant xenbus->backend_id = 0; /* Assume lack of node means dom0 */ 336094a2239SPaul Durrant } 337094a2239SPaul Durrant 338a783f8adSPaul Durrant module_call_init(MODULE_INIT_XEN_BACKEND); 339a783f8adSPaul Durrant 340c4583c8cSPaul Durrant type = xen_backend_get_types(&xenbus->backend_types); 341ba2a92dbSPaul Durrant xenbus->backend_watch = g_new(struct qemu_xs_watch *, 342ba2a92dbSPaul Durrant xenbus->backend_types); 343c4583c8cSPaul Durrant 344c4583c8cSPaul Durrant for (i = 0; i < xenbus->backend_types; i++) { 345c4583c8cSPaul Durrant char *node = g_strdup_printf("backend/%s", type[i]); 346c4583c8cSPaul Durrant 347c4583c8cSPaul Durrant xenbus->backend_watch[i] = 348ba2a92dbSPaul Durrant xs_node_watch(xenbus->xsh, node, key, xen_bus_backend_changed, 349ba2a92dbSPaul Durrant xenbus, &local_err); 350a783f8adSPaul Durrant if (local_err) { 351a783f8adSPaul Durrant /* This need not be treated as a hard error so don't propagate */ 352a783f8adSPaul Durrant error_reportf_err(local_err, 353c4583c8cSPaul Durrant "failed to set up '%s' enumeration watch: ", 354c4583c8cSPaul Durrant type[i]); 355a783f8adSPaul Durrant } 356a783f8adSPaul Durrant 357c4583c8cSPaul Durrant g_free(node); 358c4583c8cSPaul Durrant } 359c4583c8cSPaul Durrant 360c4583c8cSPaul Durrant g_free(type); 361c4583c8cSPaul Durrant g_free(key); 362094a2239SPaul Durrant return; 363094a2239SPaul Durrant 364094a2239SPaul Durrant fail: 365b69c3c21SMarkus Armbruster xen_bus_unrealize(bus); 366c4583c8cSPaul Durrant g_free(key); 367108f7bbaSPaul Durrant } 368108f7bbaSPaul Durrant 369b6af8926SPaul Durrant static void xen_bus_unplug_request(HotplugHandler *hotplug, 370b6af8926SPaul Durrant DeviceState *dev, 371b6af8926SPaul Durrant Error **errp) 372b6af8926SPaul Durrant { 373b6af8926SPaul Durrant XenDevice *xendev = XEN_DEVICE(dev); 374b6af8926SPaul Durrant 375b6af8926SPaul Durrant xen_device_unplug(xendev, errp); 376b6af8926SPaul Durrant } 377b6af8926SPaul Durrant 378108f7bbaSPaul Durrant static void xen_bus_class_init(ObjectClass *class, void *data) 379108f7bbaSPaul Durrant { 380108f7bbaSPaul Durrant BusClass *bus_class = BUS_CLASS(class); 381b6af8926SPaul Durrant HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class); 382108f7bbaSPaul Durrant 383094a2239SPaul Durrant bus_class->print_dev = xen_bus_print_dev; 384094a2239SPaul Durrant bus_class->get_dev_path = xen_bus_get_dev_path; 385108f7bbaSPaul Durrant bus_class->realize = xen_bus_realize; 386108f7bbaSPaul Durrant bus_class->unrealize = xen_bus_unrealize; 387b6af8926SPaul Durrant 388b6af8926SPaul Durrant hotplug_class->unplug_request = xen_bus_unplug_request; 389108f7bbaSPaul Durrant } 390108f7bbaSPaul Durrant 391108f7bbaSPaul Durrant static const TypeInfo xen_bus_type_info = { 392108f7bbaSPaul Durrant .name = TYPE_XEN_BUS, 393108f7bbaSPaul Durrant .parent = TYPE_BUS, 394108f7bbaSPaul Durrant .instance_size = sizeof(XenBus), 395108f7bbaSPaul Durrant .class_size = sizeof(XenBusClass), 396108f7bbaSPaul Durrant .class_init = xen_bus_class_init, 397108f7bbaSPaul Durrant .interfaces = (InterfaceInfo[]) { 398108f7bbaSPaul Durrant { TYPE_HOTPLUG_HANDLER }, 399108f7bbaSPaul Durrant { } 400108f7bbaSPaul Durrant }, 401108f7bbaSPaul Durrant }; 402108f7bbaSPaul Durrant 403b6af8926SPaul Durrant void xen_device_backend_printf(XenDevice *xendev, const char *key, 404094a2239SPaul Durrant const char *fmt, ...) 405094a2239SPaul Durrant { 406094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 407094a2239SPaul Durrant Error *local_err = NULL; 408094a2239SPaul Durrant va_list ap; 409094a2239SPaul Durrant 410094a2239SPaul Durrant g_assert(xenbus->xsh); 411094a2239SPaul Durrant 412094a2239SPaul Durrant va_start(ap, fmt); 413094a2239SPaul Durrant xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, 414094a2239SPaul Durrant &local_err, fmt, ap); 415094a2239SPaul Durrant va_end(ap); 416094a2239SPaul Durrant 417094a2239SPaul Durrant if (local_err) { 418094a2239SPaul Durrant error_report_err(local_err); 419094a2239SPaul Durrant } 420094a2239SPaul Durrant } 421094a2239SPaul Durrant 422d62449daSDaniel P. Berrangé G_GNUC_SCANF(3, 4) 42382a29e30SPaul Durrant static int xen_device_backend_scanf(XenDevice *xendev, const char *key, 42482a29e30SPaul Durrant const char *fmt, ...) 42582a29e30SPaul Durrant { 42682a29e30SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 42782a29e30SPaul Durrant va_list ap; 42882a29e30SPaul Durrant int rc; 42982a29e30SPaul Durrant 43082a29e30SPaul Durrant g_assert(xenbus->xsh); 43182a29e30SPaul Durrant 43282a29e30SPaul Durrant va_start(ap, fmt); 43382a29e30SPaul Durrant rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, 43482a29e30SPaul Durrant NULL, fmt, ap); 43582a29e30SPaul Durrant va_end(ap); 43682a29e30SPaul Durrant 43782a29e30SPaul Durrant return rc; 43882a29e30SPaul Durrant } 43982a29e30SPaul Durrant 44082a29e30SPaul Durrant void xen_device_backend_set_state(XenDevice *xendev, 441094a2239SPaul Durrant enum xenbus_state state) 442094a2239SPaul Durrant { 443094a2239SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 444094a2239SPaul Durrant 445094a2239SPaul Durrant if (xendev->backend_state == state) { 446094a2239SPaul Durrant return; 447094a2239SPaul Durrant } 448094a2239SPaul Durrant 449094a2239SPaul Durrant trace_xen_device_backend_state(type, xendev->name, 450094a2239SPaul Durrant xs_strstate(state)); 451094a2239SPaul Durrant 452094a2239SPaul Durrant xendev->backend_state = state; 453094a2239SPaul Durrant xen_device_backend_printf(xendev, "state", "%u", state); 454094a2239SPaul Durrant } 455094a2239SPaul Durrant 45682a29e30SPaul Durrant enum xenbus_state xen_device_backend_get_state(XenDevice *xendev) 45782a29e30SPaul Durrant { 45882a29e30SPaul Durrant return xendev->backend_state; 45982a29e30SPaul Durrant } 46082a29e30SPaul Durrant 461b6af8926SPaul Durrant static void xen_device_backend_set_online(XenDevice *xendev, bool online) 462b6af8926SPaul Durrant { 463b6af8926SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 464b6af8926SPaul Durrant 465b6af8926SPaul Durrant if (xendev->backend_online == online) { 466b6af8926SPaul Durrant return; 467b6af8926SPaul Durrant } 468b6af8926SPaul Durrant 469b6af8926SPaul Durrant trace_xen_device_backend_online(type, xendev->name, online); 470b6af8926SPaul Durrant 471b6af8926SPaul Durrant xendev->backend_online = online; 472b6af8926SPaul Durrant xen_device_backend_printf(xendev, "online", "%u", online); 473b6af8926SPaul Durrant } 474b6af8926SPaul Durrant 475cb323146SAnthony PERARD /* 476cb323146SAnthony PERARD * Tell from the state whether the frontend is likely alive, 477cb323146SAnthony PERARD * i.e. it will react to a change of state of the backend. 478cb323146SAnthony PERARD */ 4793809f758SPaul Durrant static bool xen_device_frontend_is_active(XenDevice *xendev) 480cb323146SAnthony PERARD { 4813809f758SPaul Durrant switch (xendev->frontend_state) { 482cb323146SAnthony PERARD case XenbusStateInitWait: 483cb323146SAnthony PERARD case XenbusStateInitialised: 484cb323146SAnthony PERARD case XenbusStateConnected: 485cb323146SAnthony PERARD case XenbusStateClosing: 486cb323146SAnthony PERARD return true; 487cb323146SAnthony PERARD default: 488cb323146SAnthony PERARD return false; 489cb323146SAnthony PERARD } 490cb323146SAnthony PERARD } 491cb323146SAnthony PERARD 492ba2a92dbSPaul Durrant static void xen_device_backend_changed(void *opaque, const char *path) 493b6af8926SPaul Durrant { 494b6af8926SPaul Durrant XenDevice *xendev = opaque; 495b6af8926SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 496b6af8926SPaul Durrant enum xenbus_state state; 497b6af8926SPaul Durrant unsigned int online; 498b6af8926SPaul Durrant 499b6af8926SPaul Durrant trace_xen_device_backend_changed(type, xendev->name); 500b6af8926SPaul Durrant 501b6af8926SPaul Durrant if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) { 502b6af8926SPaul Durrant state = XenbusStateUnknown; 503b6af8926SPaul Durrant } 504b6af8926SPaul Durrant 505b6af8926SPaul Durrant xen_device_backend_set_state(xendev, state); 506b6af8926SPaul Durrant 507b6af8926SPaul Durrant if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) { 508b6af8926SPaul Durrant online = 0; 509b6af8926SPaul Durrant } 510b6af8926SPaul Durrant 511b6af8926SPaul Durrant xen_device_backend_set_online(xendev, !!online); 512b6af8926SPaul Durrant 513b6af8926SPaul Durrant /* 514b6af8926SPaul Durrant * If the toolstack (or unplug request callback) has set the backend 515cb323146SAnthony PERARD * state to Closing, but there is no active frontend then set the 516cb323146SAnthony PERARD * backend state to Closed. 517b6af8926SPaul Durrant */ 5183809f758SPaul Durrant if (state == XenbusStateClosing && 5193809f758SPaul Durrant !xen_device_frontend_is_active(xendev)) { 520b6af8926SPaul Durrant xen_device_backend_set_state(xendev, XenbusStateClosed); 521b6af8926SPaul Durrant } 522b6af8926SPaul Durrant 523b6af8926SPaul Durrant /* 52467bc8e00SPaul Durrant * If a backend is still 'online' then we should leave it alone but, 5253809f758SPaul Durrant * if a backend is not 'online', then the device is a candidate 5263809f758SPaul Durrant * for destruction. Hence add it to the 'inactive' list to be cleaned 5273809f758SPaul Durrant * by xen_bus_cleanup(). 528b6af8926SPaul Durrant */ 5293809f758SPaul Durrant if (!online && 5303809f758SPaul Durrant (state == XenbusStateClosed || state == XenbusStateInitialising || 5313809f758SPaul Durrant state == XenbusStateInitWait || state == XenbusStateUnknown) && 5323809f758SPaul Durrant !xendev->inactive) { 5333809f758SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 534a783f8adSPaul Durrant 5353809f758SPaul Durrant xendev->inactive = true; 5363809f758SPaul Durrant QLIST_INSERT_HEAD(&xenbus->inactive_devices, xendev, list); 537a783f8adSPaul Durrant 5383809f758SPaul Durrant /* 5393809f758SPaul Durrant * Re-write the state to cause a XenBus backend_watch notification, 5403809f758SPaul Durrant * resulting in a call to xen_bus_cleanup(). 5413809f758SPaul Durrant */ 5423809f758SPaul Durrant xen_device_backend_printf(xendev, "state", "%u", state); 543a783f8adSPaul Durrant } 544b6af8926SPaul Durrant } 545b6af8926SPaul Durrant 546094a2239SPaul Durrant static void xen_device_backend_create(XenDevice *xendev, Error **errp) 547094a2239SPaul Durrant { 5481de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 549094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 550094a2239SPaul Durrant 551094a2239SPaul Durrant xendev->backend_path = xen_device_get_backend_path(xendev); 552094a2239SPaul Durrant 553094a2239SPaul Durrant g_assert(xenbus->xsh); 554094a2239SPaul Durrant 555ba2a92dbSPaul Durrant xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, 556ba2a92dbSPaul Durrant xenbus->backend_id, xendev->frontend_id, XS_PERM_READ, errp); 5571de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 5581de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to create backend: "); 559b6af8926SPaul Durrant return; 560b6af8926SPaul Durrant } 561b6af8926SPaul Durrant 562b6af8926SPaul Durrant xendev->backend_state_watch = 563ba2a92dbSPaul Durrant xs_node_watch(xendev->xsh, xendev->backend_path, 564ba2a92dbSPaul Durrant "state", xen_device_backend_changed, xendev, 5651de7096dSVladimir Sementsov-Ogievskiy errp); 5661de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 5671de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to watch backend state: "); 568b6af8926SPaul Durrant return; 569b6af8926SPaul Durrant } 570b6af8926SPaul Durrant 571b6af8926SPaul Durrant xendev->backend_online_watch = 572ba2a92dbSPaul Durrant xs_node_watch(xendev->xsh, xendev->backend_path, 573ba2a92dbSPaul Durrant "online", xen_device_backend_changed, xendev, 5741de7096dSVladimir Sementsov-Ogievskiy errp); 5751de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 5761de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to watch backend online: "); 577b6af8926SPaul Durrant return; 578094a2239SPaul Durrant } 579094a2239SPaul Durrant } 580094a2239SPaul Durrant 581094a2239SPaul Durrant static void xen_device_backend_destroy(XenDevice *xendev) 582094a2239SPaul Durrant { 583094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 584094a2239SPaul Durrant Error *local_err = NULL; 585094a2239SPaul Durrant 586b6af8926SPaul Durrant if (xendev->backend_online_watch) { 587ba2a92dbSPaul Durrant xs_node_unwatch(xendev->xsh, xendev->backend_online_watch); 588b6af8926SPaul Durrant xendev->backend_online_watch = NULL; 589b6af8926SPaul Durrant } 590b6af8926SPaul Durrant 591b6af8926SPaul Durrant if (xendev->backend_state_watch) { 592ba2a92dbSPaul Durrant xs_node_unwatch(xendev->xsh, xendev->backend_state_watch); 593b6af8926SPaul Durrant xendev->backend_state_watch = NULL; 594b6af8926SPaul Durrant } 595b6af8926SPaul Durrant 596094a2239SPaul Durrant if (!xendev->backend_path) { 597094a2239SPaul Durrant return; 598094a2239SPaul Durrant } 599094a2239SPaul Durrant 600094a2239SPaul Durrant g_assert(xenbus->xsh); 601094a2239SPaul Durrant 602094a2239SPaul Durrant xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->backend_path, 603094a2239SPaul Durrant &local_err); 604094a2239SPaul Durrant g_free(xendev->backend_path); 605094a2239SPaul Durrant xendev->backend_path = NULL; 606094a2239SPaul Durrant 607094a2239SPaul Durrant if (local_err) { 608094a2239SPaul Durrant error_report_err(local_err); 609094a2239SPaul Durrant } 610094a2239SPaul Durrant } 611094a2239SPaul Durrant 612b6af8926SPaul Durrant void xen_device_frontend_printf(XenDevice *xendev, const char *key, 613094a2239SPaul Durrant const char *fmt, ...) 614094a2239SPaul Durrant { 615094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 616094a2239SPaul Durrant Error *local_err = NULL; 617094a2239SPaul Durrant va_list ap; 618094a2239SPaul Durrant 619094a2239SPaul Durrant g_assert(xenbus->xsh); 620094a2239SPaul Durrant 621094a2239SPaul Durrant va_start(ap, fmt); 622094a2239SPaul Durrant xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, 623094a2239SPaul Durrant &local_err, fmt, ap); 624094a2239SPaul Durrant va_end(ap); 625094a2239SPaul Durrant 626094a2239SPaul Durrant if (local_err) { 627094a2239SPaul Durrant error_report_err(local_err); 628094a2239SPaul Durrant } 629094a2239SPaul Durrant } 630094a2239SPaul Durrant 631b6af8926SPaul Durrant int xen_device_frontend_scanf(XenDevice *xendev, const char *key, 63282a29e30SPaul Durrant const char *fmt, ...) 63382a29e30SPaul Durrant { 63482a29e30SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 63582a29e30SPaul Durrant va_list ap; 63682a29e30SPaul Durrant int rc; 63782a29e30SPaul Durrant 63882a29e30SPaul Durrant g_assert(xenbus->xsh); 63982a29e30SPaul Durrant 64082a29e30SPaul Durrant va_start(ap, fmt); 64182a29e30SPaul Durrant rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, 64282a29e30SPaul Durrant NULL, fmt, ap); 64382a29e30SPaul Durrant va_end(ap); 64482a29e30SPaul Durrant 64582a29e30SPaul Durrant return rc; 64682a29e30SPaul Durrant } 64782a29e30SPaul Durrant 648094a2239SPaul Durrant static void xen_device_frontend_set_state(XenDevice *xendev, 649705be570SAnthony PERARD enum xenbus_state state, 650705be570SAnthony PERARD bool publish) 651094a2239SPaul Durrant { 652094a2239SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 653094a2239SPaul Durrant 654094a2239SPaul Durrant if (xendev->frontend_state == state) { 655094a2239SPaul Durrant return; 656094a2239SPaul Durrant } 657094a2239SPaul Durrant 658094a2239SPaul Durrant trace_xen_device_frontend_state(type, xendev->name, 659094a2239SPaul Durrant xs_strstate(state)); 660094a2239SPaul Durrant 661094a2239SPaul Durrant xendev->frontend_state = state; 662705be570SAnthony PERARD if (publish) { 663094a2239SPaul Durrant xen_device_frontend_printf(xendev, "state", "%u", state); 664094a2239SPaul Durrant } 665705be570SAnthony PERARD } 666094a2239SPaul Durrant 667ba2a92dbSPaul Durrant static void xen_device_frontend_changed(void *opaque, const char *path) 66882a29e30SPaul Durrant { 66982a29e30SPaul Durrant XenDevice *xendev = opaque; 67082a29e30SPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 67182a29e30SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 67282a29e30SPaul Durrant enum xenbus_state state; 67382a29e30SPaul Durrant 67482a29e30SPaul Durrant trace_xen_device_frontend_changed(type, xendev->name); 67582a29e30SPaul Durrant 67682a29e30SPaul Durrant if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) { 67782a29e30SPaul Durrant state = XenbusStateUnknown; 67882a29e30SPaul Durrant } 67982a29e30SPaul Durrant 680705be570SAnthony PERARD xen_device_frontend_set_state(xendev, state, false); 68182a29e30SPaul Durrant 68267bc8e00SPaul Durrant if (state == XenbusStateInitialising && 68367bc8e00SPaul Durrant xendev->backend_state == XenbusStateClosed && 68467bc8e00SPaul Durrant xendev->backend_online) { 68567bc8e00SPaul Durrant /* 68667bc8e00SPaul Durrant * The frontend is re-initializing so switch back to 68767bc8e00SPaul Durrant * InitWait. 68867bc8e00SPaul Durrant */ 68967bc8e00SPaul Durrant xen_device_backend_set_state(xendev, XenbusStateInitWait); 69067bc8e00SPaul Durrant return; 69167bc8e00SPaul Durrant } 69267bc8e00SPaul Durrant 69382a29e30SPaul Durrant if (xendev_class->frontend_changed) { 69482a29e30SPaul Durrant Error *local_err = NULL; 69582a29e30SPaul Durrant 69682a29e30SPaul Durrant xendev_class->frontend_changed(xendev, state, &local_err); 69782a29e30SPaul Durrant 69882a29e30SPaul Durrant if (local_err) { 69982a29e30SPaul Durrant error_reportf_err(local_err, "frontend change error: "); 70082a29e30SPaul Durrant } 70182a29e30SPaul Durrant } 70282a29e30SPaul Durrant } 70382a29e30SPaul Durrant 7046bd6b955SMark Syms static bool xen_device_frontend_exists(XenDevice *xendev) 7056bd6b955SMark Syms { 7066bd6b955SMark Syms enum xenbus_state state; 7076bd6b955SMark Syms 7086bd6b955SMark Syms return (xen_device_frontend_scanf(xendev, "state", "%u", &state) == 1); 7096bd6b955SMark Syms } 7106bd6b955SMark Syms 711094a2239SPaul Durrant static void xen_device_frontend_create(XenDevice *xendev, Error **errp) 712094a2239SPaul Durrant { 7131de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 714094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 715523b6b3aSDavid Woodhouse XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 716094a2239SPaul Durrant 717523b6b3aSDavid Woodhouse if (xendev_class->get_frontend_path) { 718523b6b3aSDavid Woodhouse xendev->frontend_path = xendev_class->get_frontend_path(xendev, errp); 719523b6b3aSDavid Woodhouse if (!xendev->frontend_path) { 720523b6b3aSDavid Woodhouse error_prepend(errp, "failed to create frontend: "); 721523b6b3aSDavid Woodhouse return; 722523b6b3aSDavid Woodhouse } 723523b6b3aSDavid Woodhouse } else { 724094a2239SPaul Durrant xendev->frontend_path = xen_device_get_frontend_path(xendev); 725523b6b3aSDavid Woodhouse } 726094a2239SPaul Durrant 7276bd6b955SMark Syms /* 7286bd6b955SMark Syms * The frontend area may have already been created by a legacy 7296bd6b955SMark Syms * toolstack. 7306bd6b955SMark Syms */ 7316bd6b955SMark Syms if (!xen_device_frontend_exists(xendev)) { 732094a2239SPaul Durrant g_assert(xenbus->xsh); 733094a2239SPaul Durrant 734ba2a92dbSPaul Durrant xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, 735ba2a92dbSPaul Durrant xendev->frontend_id, xenbus->backend_id, 736ba2a92dbSPaul Durrant XS_PERM_READ | XS_PERM_WRITE, errp); 7371de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 7381de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to create frontend: "); 73982a29e30SPaul Durrant return; 74082a29e30SPaul Durrant } 7416bd6b955SMark Syms } 74282a29e30SPaul Durrant 74382a29e30SPaul Durrant xendev->frontend_state_watch = 744ba2a92dbSPaul Durrant xs_node_watch(xendev->xsh, xendev->frontend_path, "state", 745ba2a92dbSPaul Durrant xen_device_frontend_changed, xendev, errp); 7461de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 7471de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to watch frontend state: "); 748094a2239SPaul Durrant } 749094a2239SPaul Durrant } 750094a2239SPaul Durrant 751094a2239SPaul Durrant static void xen_device_frontend_destroy(XenDevice *xendev) 752094a2239SPaul Durrant { 753094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 754094a2239SPaul Durrant Error *local_err = NULL; 755094a2239SPaul Durrant 75682a29e30SPaul Durrant if (xendev->frontend_state_watch) { 757ba2a92dbSPaul Durrant xs_node_unwatch(xendev->xsh, xendev->frontend_state_watch); 75882a29e30SPaul Durrant xendev->frontend_state_watch = NULL; 75982a29e30SPaul Durrant } 76082a29e30SPaul Durrant 761094a2239SPaul Durrant if (!xendev->frontend_path) { 762094a2239SPaul Durrant return; 763094a2239SPaul Durrant } 764094a2239SPaul Durrant 765094a2239SPaul Durrant g_assert(xenbus->xsh); 766094a2239SPaul Durrant 767094a2239SPaul Durrant xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->frontend_path, 768094a2239SPaul Durrant &local_err); 769094a2239SPaul Durrant g_free(xendev->frontend_path); 770094a2239SPaul Durrant xendev->frontend_path = NULL; 771094a2239SPaul Durrant 772094a2239SPaul Durrant if (local_err) { 773094a2239SPaul Durrant error_report_err(local_err); 774094a2239SPaul Durrant } 775094a2239SPaul Durrant } 776094a2239SPaul Durrant 7774b34b5b1SPaul Durrant void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, 7784b34b5b1SPaul Durrant Error **errp) 7794b34b5b1SPaul Durrant { 780c412ba47SDavid Woodhouse if (qemu_xen_gnttab_set_max_grants(xendev->xgth, nr_refs)) { 7814b34b5b1SPaul Durrant error_setg_errno(errp, errno, "xengnttab_set_max_grants failed"); 7824b34b5b1SPaul Durrant } 7834b34b5b1SPaul Durrant } 7844b34b5b1SPaul Durrant 7854b34b5b1SPaul Durrant void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, 7864b34b5b1SPaul Durrant unsigned int nr_refs, int prot, 7874b34b5b1SPaul Durrant Error **errp) 7884b34b5b1SPaul Durrant { 789c412ba47SDavid Woodhouse void *map = qemu_xen_gnttab_map_refs(xendev->xgth, nr_refs, 790c412ba47SDavid Woodhouse xendev->frontend_id, refs, prot); 7914b34b5b1SPaul Durrant 7924b34b5b1SPaul Durrant if (!map) { 7934b34b5b1SPaul Durrant error_setg_errno(errp, errno, 7944b34b5b1SPaul Durrant "xengnttab_map_domain_grant_refs failed"); 7954b34b5b1SPaul Durrant } 7964b34b5b1SPaul Durrant 7974b34b5b1SPaul Durrant return map; 7984b34b5b1SPaul Durrant } 7994b34b5b1SPaul Durrant 800f80fad16SDavid Woodhouse void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, uint32_t *refs, 8014b34b5b1SPaul Durrant unsigned int nr_refs, Error **errp) 8024b34b5b1SPaul Durrant { 803f80fad16SDavid Woodhouse if (qemu_xen_gnttab_unmap(xendev->xgth, map, refs, nr_refs)) { 8044b34b5b1SPaul Durrant error_setg_errno(errp, errno, "xengnttab_unmap failed"); 8054b34b5b1SPaul Durrant } 8064b34b5b1SPaul Durrant } 8074b34b5b1SPaul Durrant 8084b34b5b1SPaul Durrant void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, 8094b34b5b1SPaul Durrant XenDeviceGrantCopySegment segs[], 8104b34b5b1SPaul Durrant unsigned int nr_segs, Error **errp) 8114b34b5b1SPaul Durrant { 812c412ba47SDavid Woodhouse qemu_xen_gnttab_grant_copy(xendev->xgth, to_domain, xendev->frontend_id, 813c412ba47SDavid Woodhouse (XenGrantCopySegment *)segs, nr_segs, errp); 8144b34b5b1SPaul Durrant } 8154b34b5b1SPaul Durrant 816a3d669c8SPaul Durrant struct XenEventChannel { 817c0b336eaSPaul Durrant QLIST_ENTRY(XenEventChannel) list; 81883361a8aSPaul Durrant AioContext *ctx; 819c0b336eaSPaul Durrant xenevtchn_handle *xeh; 820a3d669c8SPaul Durrant evtchn_port_t local_port; 821a3d669c8SPaul Durrant XenEventHandler handler; 822a3d669c8SPaul Durrant void *opaque; 823a3d669c8SPaul Durrant }; 824a3d669c8SPaul Durrant 825345f42b4SPaul Durrant static bool xen_device_poll(void *opaque) 826345f42b4SPaul Durrant { 827345f42b4SPaul Durrant XenEventChannel *channel = opaque; 828345f42b4SPaul Durrant 829345f42b4SPaul Durrant return channel->handler(channel->opaque); 830345f42b4SPaul Durrant } 831345f42b4SPaul Durrant 832c0b336eaSPaul Durrant static void xen_device_event(void *opaque) 833a3d669c8SPaul Durrant { 834c0b336eaSPaul Durrant XenEventChannel *channel = opaque; 835b6cacfeaSDavid Woodhouse unsigned long port = qemu_xen_evtchn_pending(channel->xeh); 836a3d669c8SPaul Durrant 837a3d669c8SPaul Durrant if (port == channel->local_port) { 838345f42b4SPaul Durrant xen_device_poll(channel); 839c0b336eaSPaul Durrant 840b6cacfeaSDavid Woodhouse qemu_xen_evtchn_unmask(channel->xeh, port); 841a3d669c8SPaul Durrant } 842a3d669c8SPaul Durrant } 843a3d669c8SPaul Durrant 84432d0b7beSPaul Durrant void xen_device_set_event_channel_context(XenDevice *xendev, 84532d0b7beSPaul Durrant XenEventChannel *channel, 84683361a8aSPaul Durrant AioContext *ctx, 84732d0b7beSPaul Durrant Error **errp) 84832d0b7beSPaul Durrant { 84932d0b7beSPaul Durrant if (!channel) { 85032d0b7beSPaul Durrant error_setg(errp, "bad channel"); 85132d0b7beSPaul Durrant return; 85232d0b7beSPaul Durrant } 85332d0b7beSPaul Durrant 85432d0b7beSPaul Durrant if (channel->ctx) 85560f782b6SStefan Hajnoczi aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), 856826cc324SStefan Hajnoczi NULL, NULL, NULL, NULL, NULL); 85732d0b7beSPaul Durrant 85832d0b7beSPaul Durrant channel->ctx = ctx; 859f6eac904SStefan Hajnoczi if (ctx) { 860f6eac904SStefan Hajnoczi aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), 86160f782b6SStefan Hajnoczi xen_device_event, NULL, xen_device_poll, NULL, 86260f782b6SStefan Hajnoczi channel); 863f6eac904SStefan Hajnoczi } 86432d0b7beSPaul Durrant } 86532d0b7beSPaul Durrant 86632d0b7beSPaul Durrant XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, 867a3d669c8SPaul Durrant unsigned int port, 868a3d669c8SPaul Durrant XenEventHandler handler, 869a3d669c8SPaul Durrant void *opaque, Error **errp) 870a3d669c8SPaul Durrant { 871a3d669c8SPaul Durrant XenEventChannel *channel = g_new0(XenEventChannel, 1); 872a3d669c8SPaul Durrant xenevtchn_port_or_error_t local_port; 873a3d669c8SPaul Durrant 874b6cacfeaSDavid Woodhouse channel->xeh = qemu_xen_evtchn_open(); 875c0b336eaSPaul Durrant if (!channel->xeh) { 876c0b336eaSPaul Durrant error_setg_errno(errp, errno, "failed xenevtchn_open"); 877c0b336eaSPaul Durrant goto fail; 878c0b336eaSPaul Durrant } 879c0b336eaSPaul Durrant 880b6cacfeaSDavid Woodhouse local_port = qemu_xen_evtchn_bind_interdomain(channel->xeh, 881a3d669c8SPaul Durrant xendev->frontend_id, 882a3d669c8SPaul Durrant port); 883a3d669c8SPaul Durrant if (local_port < 0) { 884a3d669c8SPaul Durrant error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed"); 885c0b336eaSPaul Durrant goto fail; 886a3d669c8SPaul Durrant } 887a3d669c8SPaul Durrant 888a3d669c8SPaul Durrant channel->local_port = local_port; 889a3d669c8SPaul Durrant channel->handler = handler; 890a3d669c8SPaul Durrant channel->opaque = opaque; 891a3d669c8SPaul Durrant 89232d0b7beSPaul Durrant /* Only reason for failure is a NULL channel */ 89332d0b7beSPaul Durrant xen_device_set_event_channel_context(xendev, channel, 89432d0b7beSPaul Durrant qemu_get_aio_context(), 89532d0b7beSPaul Durrant &error_abort); 896c0b336eaSPaul Durrant 897c0b336eaSPaul Durrant QLIST_INSERT_HEAD(&xendev->event_channels, channel, list); 898a3d669c8SPaul Durrant 899a3d669c8SPaul Durrant return channel; 900c0b336eaSPaul Durrant 901c0b336eaSPaul Durrant fail: 902c0b336eaSPaul Durrant if (channel->xeh) { 903b6cacfeaSDavid Woodhouse qemu_xen_evtchn_close(channel->xeh); 904c0b336eaSPaul Durrant } 905c0b336eaSPaul Durrant 906c0b336eaSPaul Durrant g_free(channel); 907c0b336eaSPaul Durrant 908c0b336eaSPaul Durrant return NULL; 909a3d669c8SPaul Durrant } 910a3d669c8SPaul Durrant 911a3d669c8SPaul Durrant void xen_device_notify_event_channel(XenDevice *xendev, 912a3d669c8SPaul Durrant XenEventChannel *channel, 913a3d669c8SPaul Durrant Error **errp) 914a3d669c8SPaul Durrant { 915a3d669c8SPaul Durrant if (!channel) { 916a3d669c8SPaul Durrant error_setg(errp, "bad channel"); 917a3d669c8SPaul Durrant return; 918a3d669c8SPaul Durrant } 919a3d669c8SPaul Durrant 920b6cacfeaSDavid Woodhouse if (qemu_xen_evtchn_notify(channel->xeh, channel->local_port) < 0) { 921a3d669c8SPaul Durrant error_setg_errno(errp, errno, "xenevtchn_notify failed"); 922a3d669c8SPaul Durrant } 923a3d669c8SPaul Durrant } 924a3d669c8SPaul Durrant 925a3d669c8SPaul Durrant void xen_device_unbind_event_channel(XenDevice *xendev, 926a3d669c8SPaul Durrant XenEventChannel *channel, 927a3d669c8SPaul Durrant Error **errp) 928a3d669c8SPaul Durrant { 929a3d669c8SPaul Durrant if (!channel) { 930a3d669c8SPaul Durrant error_setg(errp, "bad channel"); 931a3d669c8SPaul Durrant return; 932a3d669c8SPaul Durrant } 933a3d669c8SPaul Durrant 934c0b336eaSPaul Durrant QLIST_REMOVE(channel, list); 935a3d669c8SPaul Durrant 93690006660SAnthony PERARD if (channel->ctx) { 93760f782b6SStefan Hajnoczi aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), 938826cc324SStefan Hajnoczi NULL, NULL, NULL, NULL, NULL); 93990006660SAnthony PERARD } 940c0b336eaSPaul Durrant 941b6cacfeaSDavid Woodhouse if (qemu_xen_evtchn_unbind(channel->xeh, channel->local_port) < 0) { 942a3d669c8SPaul Durrant error_setg_errno(errp, errno, "xenevtchn_unbind failed"); 943a3d669c8SPaul Durrant } 944a3d669c8SPaul Durrant 945b6cacfeaSDavid Woodhouse qemu_xen_evtchn_close(channel->xeh); 946a3d669c8SPaul Durrant g_free(channel); 947a3d669c8SPaul Durrant } 948a3d669c8SPaul Durrant 949b69c3c21SMarkus Armbruster static void xen_device_unrealize(DeviceState *dev) 950108f7bbaSPaul Durrant { 951108f7bbaSPaul Durrant XenDevice *xendev = XEN_DEVICE(dev); 952108f7bbaSPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 953108f7bbaSPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 954c0b336eaSPaul Durrant XenEventChannel *channel, *next; 955108f7bbaSPaul Durrant 956094a2239SPaul Durrant if (!xendev->name) { 957094a2239SPaul Durrant return; 958094a2239SPaul Durrant } 959094a2239SPaul Durrant 960094a2239SPaul Durrant trace_xen_device_unrealize(type, xendev->name); 961094a2239SPaul Durrant 962094a2239SPaul Durrant if (xendev->exit.notify) { 963094a2239SPaul Durrant qemu_remove_exit_notifier(&xendev->exit); 964094a2239SPaul Durrant xendev->exit.notify = NULL; 965094a2239SPaul Durrant } 966108f7bbaSPaul Durrant 967108f7bbaSPaul Durrant if (xendev_class->unrealize) { 968b69c3c21SMarkus Armbruster xendev_class->unrealize(xendev); 969108f7bbaSPaul Durrant } 970094a2239SPaul Durrant 971c0b336eaSPaul Durrant /* Make sure all event channels are cleaned up */ 972c0b336eaSPaul Durrant QLIST_FOREACH_SAFE(channel, &xendev->event_channels, list, next) { 973c0b336eaSPaul Durrant xen_device_unbind_event_channel(xendev, channel, NULL); 974c0b336eaSPaul Durrant } 975c0b336eaSPaul Durrant 976094a2239SPaul Durrant xen_device_frontend_destroy(xendev); 977094a2239SPaul Durrant xen_device_backend_destroy(xendev); 978094a2239SPaul Durrant 9794b34b5b1SPaul Durrant if (xendev->xgth) { 980c412ba47SDavid Woodhouse qemu_xen_gnttab_close(xendev->xgth); 9814b34b5b1SPaul Durrant xendev->xgth = NULL; 9824b34b5b1SPaul Durrant } 9834b34b5b1SPaul Durrant 984d198b711SPaul Durrant if (xendev->xsh) { 985ba2a92dbSPaul Durrant qemu_xen_xs_close(xendev->xsh); 986d198b711SPaul Durrant xendev->xsh = NULL; 987d198b711SPaul Durrant } 988d198b711SPaul Durrant 989094a2239SPaul Durrant g_free(xendev->name); 990094a2239SPaul Durrant xendev->name = NULL; 991094a2239SPaul Durrant } 992094a2239SPaul Durrant 993094a2239SPaul Durrant static void xen_device_exit(Notifier *n, void *data) 994094a2239SPaul Durrant { 995094a2239SPaul Durrant XenDevice *xendev = container_of(n, XenDevice, exit); 996094a2239SPaul Durrant 997b69c3c21SMarkus Armbruster xen_device_unrealize(DEVICE(xendev)); 998108f7bbaSPaul Durrant } 999108f7bbaSPaul Durrant 1000108f7bbaSPaul Durrant static void xen_device_realize(DeviceState *dev, Error **errp) 1001108f7bbaSPaul Durrant { 10021de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 1003108f7bbaSPaul Durrant XenDevice *xendev = XEN_DEVICE(dev); 1004108f7bbaSPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 1005094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 1006108f7bbaSPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 1007108f7bbaSPaul Durrant 1008094a2239SPaul Durrant if (xendev->frontend_id == DOMID_INVALID) { 1009094a2239SPaul Durrant xendev->frontend_id = xen_domid; 1010094a2239SPaul Durrant } 1011094a2239SPaul Durrant 1012094a2239SPaul Durrant if (xendev->frontend_id >= DOMID_FIRST_RESERVED) { 1013094a2239SPaul Durrant error_setg(errp, "invalid frontend-id"); 1014094a2239SPaul Durrant goto unrealize; 1015094a2239SPaul Durrant } 1016094a2239SPaul Durrant 1017094a2239SPaul Durrant if (!xendev_class->get_name) { 1018094a2239SPaul Durrant error_setg(errp, "get_name method not implemented"); 1019094a2239SPaul Durrant goto unrealize; 1020094a2239SPaul Durrant } 1021094a2239SPaul Durrant 10221de7096dSVladimir Sementsov-Ogievskiy xendev->name = xendev_class->get_name(xendev, errp); 10231de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 10241de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to get device name: "); 1025094a2239SPaul Durrant goto unrealize; 1026094a2239SPaul Durrant } 1027094a2239SPaul Durrant 1028094a2239SPaul Durrant trace_xen_device_realize(type, xendev->name); 1029094a2239SPaul Durrant 1030ba2a92dbSPaul Durrant xendev->xsh = qemu_xen_xs_open(); 1031d198b711SPaul Durrant if (!xendev->xsh) { 1032d198b711SPaul Durrant error_setg_errno(errp, errno, "failed xs_open"); 1033d198b711SPaul Durrant goto unrealize; 1034d198b711SPaul Durrant } 1035d198b711SPaul Durrant 1036c412ba47SDavid Woodhouse xendev->xgth = qemu_xen_gnttab_open(); 10374b34b5b1SPaul Durrant if (!xendev->xgth) { 10384b34b5b1SPaul Durrant error_setg_errno(errp, errno, "failed xengnttab_open"); 10394b34b5b1SPaul Durrant goto unrealize; 10404b34b5b1SPaul Durrant } 10414b34b5b1SPaul Durrant 10421de7096dSVladimir Sementsov-Ogievskiy xen_device_backend_create(xendev, errp); 10431de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 1044094a2239SPaul Durrant goto unrealize; 1045094a2239SPaul Durrant } 1046094a2239SPaul Durrant 10471de7096dSVladimir Sementsov-Ogievskiy xen_device_frontend_create(xendev, errp); 10481de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 1049094a2239SPaul Durrant goto unrealize; 1050094a2239SPaul Durrant } 1051108f7bbaSPaul Durrant 1052094a2239SPaul Durrant xen_device_backend_printf(xendev, "frontend", "%s", 1053094a2239SPaul Durrant xendev->frontend_path); 1054094a2239SPaul Durrant xen_device_backend_printf(xendev, "frontend-id", "%u", 1055094a2239SPaul Durrant xendev->frontend_id); 1056094a2239SPaul Durrant xen_device_backend_printf(xendev, "hotplug-status", "connected"); 1057094a2239SPaul Durrant 1058b6af8926SPaul Durrant xen_device_backend_set_online(xendev, true); 1059094a2239SPaul Durrant xen_device_backend_set_state(xendev, XenbusStateInitWait); 1060094a2239SPaul Durrant 10616bd6b955SMark Syms if (!xen_device_frontend_exists(xendev)) { 1062094a2239SPaul Durrant xen_device_frontend_printf(xendev, "backend", "%s", 1063094a2239SPaul Durrant xendev->backend_path); 1064094a2239SPaul Durrant xen_device_frontend_printf(xendev, "backend-id", "%u", 1065094a2239SPaul Durrant xenbus->backend_id); 1066094a2239SPaul Durrant 1067705be570SAnthony PERARD xen_device_frontend_set_state(xendev, XenbusStateInitialising, true); 10686bd6b955SMark Syms } 1069094a2239SPaul Durrant 1070240cc113SPaul Durrant if (xendev_class->realize) { 1071240cc113SPaul Durrant xendev_class->realize(xendev, errp); 1072240cc113SPaul Durrant if (*errp) { 1073240cc113SPaul Durrant goto unrealize; 1074240cc113SPaul Durrant } 1075240cc113SPaul Durrant } 1076240cc113SPaul Durrant 1077094a2239SPaul Durrant xendev->exit.notify = xen_device_exit; 1078094a2239SPaul Durrant qemu_add_exit_notifier(&xendev->exit); 1079108f7bbaSPaul Durrant return; 1080108f7bbaSPaul Durrant 1081108f7bbaSPaul Durrant unrealize: 1082b69c3c21SMarkus Armbruster xen_device_unrealize(dev); 1083108f7bbaSPaul Durrant } 1084108f7bbaSPaul Durrant 1085094a2239SPaul Durrant static Property xen_device_props[] = { 1086094a2239SPaul Durrant DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id, 1087094a2239SPaul Durrant DOMID_INVALID), 1088094a2239SPaul Durrant DEFINE_PROP_END_OF_LIST() 1089094a2239SPaul Durrant }; 1090094a2239SPaul Durrant 1091108f7bbaSPaul Durrant static void xen_device_class_init(ObjectClass *class, void *data) 1092108f7bbaSPaul Durrant { 1093108f7bbaSPaul Durrant DeviceClass *dev_class = DEVICE_CLASS(class); 1094108f7bbaSPaul Durrant 1095108f7bbaSPaul Durrant dev_class->realize = xen_device_realize; 1096108f7bbaSPaul Durrant dev_class->unrealize = xen_device_unrealize; 10974f67d30bSMarc-André Lureau device_class_set_props(dev_class, xen_device_props); 1098108f7bbaSPaul Durrant dev_class->bus_type = TYPE_XEN_BUS; 1099108f7bbaSPaul Durrant } 1100108f7bbaSPaul Durrant 1101108f7bbaSPaul Durrant static const TypeInfo xen_device_type_info = { 1102108f7bbaSPaul Durrant .name = TYPE_XEN_DEVICE, 1103108f7bbaSPaul Durrant .parent = TYPE_DEVICE, 1104108f7bbaSPaul Durrant .instance_size = sizeof(XenDevice), 1105108f7bbaSPaul Durrant .abstract = true, 1106108f7bbaSPaul Durrant .class_size = sizeof(XenDeviceClass), 1107108f7bbaSPaul Durrant .class_init = xen_device_class_init, 1108108f7bbaSPaul Durrant }; 1109108f7bbaSPaul Durrant 1110108f7bbaSPaul Durrant typedef struct XenBridge { 1111108f7bbaSPaul Durrant SysBusDevice busdev; 1112108f7bbaSPaul Durrant } XenBridge; 1113108f7bbaSPaul Durrant 1114108f7bbaSPaul Durrant #define TYPE_XEN_BRIDGE "xen-bridge" 1115108f7bbaSPaul Durrant 1116108f7bbaSPaul Durrant static const TypeInfo xen_bridge_type_info = { 1117108f7bbaSPaul Durrant .name = TYPE_XEN_BRIDGE, 1118108f7bbaSPaul Durrant .parent = TYPE_SYS_BUS_DEVICE, 1119108f7bbaSPaul Durrant .instance_size = sizeof(XenBridge), 1120108f7bbaSPaul Durrant }; 1121108f7bbaSPaul Durrant 1122108f7bbaSPaul Durrant static void xen_register_types(void) 1123108f7bbaSPaul Durrant { 1124108f7bbaSPaul Durrant type_register_static(&xen_bridge_type_info); 1125108f7bbaSPaul Durrant type_register_static(&xen_bus_type_info); 1126108f7bbaSPaul Durrant type_register_static(&xen_device_type_info); 1127108f7bbaSPaul Durrant } 1128108f7bbaSPaul Durrant 1129108f7bbaSPaul Durrant type_init(xen_register_types) 1130108f7bbaSPaul Durrant 1131108f7bbaSPaul Durrant void xen_bus_init(void) 1132108f7bbaSPaul Durrant { 11333e80f690SMarkus Armbruster DeviceState *dev = qdev_new(TYPE_XEN_BRIDGE); 11349388d170SPeter Maydell BusState *bus = qbus_new(TYPE_XEN_BUS, dev, NULL); 1135108f7bbaSPaul Durrant 11363c6ef471SMarkus Armbruster sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 1137cd7c8660SMarkus Armbruster qbus_set_bus_hotplug_handler(bus); 1138108f7bbaSPaul Durrant } 1139