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" 22*7d6eff13SDavid Woodhouse #include "net/net.h" 23108f7bbaSPaul Durrant #include "trace.h" 24108f7bbaSPaul Durrant 25094a2239SPaul Durrant static char *xen_device_get_backend_path(XenDevice *xendev) 26094a2239SPaul Durrant { 27094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 28094a2239SPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 29094a2239SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 30094a2239SPaul Durrant const char *backend = xendev_class->backend; 31094a2239SPaul Durrant 32094a2239SPaul Durrant if (!backend) { 33094a2239SPaul Durrant backend = type; 34094a2239SPaul Durrant } 35094a2239SPaul Durrant 36094a2239SPaul Durrant return g_strdup_printf("/local/domain/%u/backend/%s/%u/%s", 37094a2239SPaul Durrant xenbus->backend_id, backend, xendev->frontend_id, 38094a2239SPaul Durrant xendev->name); 39094a2239SPaul Durrant } 40094a2239SPaul Durrant 41094a2239SPaul Durrant static char *xen_device_get_frontend_path(XenDevice *xendev) 42094a2239SPaul Durrant { 43094a2239SPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 44094a2239SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 45094a2239SPaul Durrant const char *device = xendev_class->device; 46094a2239SPaul Durrant 47094a2239SPaul Durrant if (!device) { 48094a2239SPaul Durrant device = type; 49094a2239SPaul Durrant } 50094a2239SPaul Durrant 51094a2239SPaul Durrant return g_strdup_printf("/local/domain/%u/device/%s/%s", 52094a2239SPaul Durrant xendev->frontend_id, device, xendev->name); 53094a2239SPaul Durrant } 54094a2239SPaul Durrant 55b6af8926SPaul Durrant static void xen_device_unplug(XenDevice *xendev, Error **errp) 56b6af8926SPaul Durrant { 571de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 58b6af8926SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 59b6af8926SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 60b6af8926SPaul Durrant xs_transaction_t tid; 61b6af8926SPaul Durrant 62b6af8926SPaul Durrant trace_xen_device_unplug(type, xendev->name); 63b6af8926SPaul Durrant 64b6af8926SPaul Durrant /* Mimic the way the Xen toolstack does an unplug */ 65b6af8926SPaul Durrant again: 66ba2a92dbSPaul Durrant tid = qemu_xen_xs_transaction_start(xenbus->xsh); 67b6af8926SPaul Durrant if (tid == XBT_NULL) { 68b6af8926SPaul Durrant error_setg_errno(errp, errno, "failed xs_transaction_start"); 69b6af8926SPaul Durrant return; 70b6af8926SPaul Durrant } 71b6af8926SPaul Durrant 72b6af8926SPaul Durrant xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "online", 731de7096dSVladimir Sementsov-Ogievskiy errp, "%u", 0); 741de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 75b6af8926SPaul Durrant goto abort; 76b6af8926SPaul Durrant } 77b6af8926SPaul Durrant 78b6af8926SPaul Durrant xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "state", 791de7096dSVladimir Sementsov-Ogievskiy errp, "%u", XenbusStateClosing); 801de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 81b6af8926SPaul Durrant goto abort; 82b6af8926SPaul Durrant } 83b6af8926SPaul Durrant 84ba2a92dbSPaul Durrant if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { 85b6af8926SPaul Durrant if (errno == EAGAIN) { 86b6af8926SPaul Durrant goto again; 87b6af8926SPaul Durrant } 88b6af8926SPaul Durrant 89b6af8926SPaul Durrant error_setg_errno(errp, errno, "failed xs_transaction_end"); 90b6af8926SPaul Durrant } 91b6af8926SPaul Durrant 92b6af8926SPaul Durrant return; 93b6af8926SPaul Durrant 94b6af8926SPaul Durrant abort: 95b6af8926SPaul Durrant /* 96b6af8926SPaul Durrant * We only abort if there is already a failure so ignore any error 97b6af8926SPaul Durrant * from ending the transaction. 98b6af8926SPaul Durrant */ 99ba2a92dbSPaul Durrant qemu_xen_xs_transaction_end(xenbus->xsh, tid, true); 100b6af8926SPaul Durrant } 101b6af8926SPaul Durrant 102094a2239SPaul Durrant static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent) 103094a2239SPaul Durrant { 104094a2239SPaul Durrant XenDevice *xendev = XEN_DEVICE(dev); 105094a2239SPaul Durrant 106094a2239SPaul Durrant monitor_printf(mon, "%*sname = '%s' frontend_id = %u\n", 107094a2239SPaul Durrant indent, "", xendev->name, xendev->frontend_id); 108094a2239SPaul Durrant } 109094a2239SPaul Durrant 110094a2239SPaul Durrant static char *xen_bus_get_dev_path(DeviceState *dev) 111094a2239SPaul Durrant { 112094a2239SPaul Durrant return xen_device_get_backend_path(XEN_DEVICE(dev)); 113094a2239SPaul Durrant } 114094a2239SPaul Durrant 115a783f8adSPaul Durrant static void xen_bus_backend_create(XenBus *xenbus, const char *type, 116a783f8adSPaul Durrant const char *name, char *path, 117a783f8adSPaul Durrant Error **errp) 118a783f8adSPaul Durrant { 1191de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 120a783f8adSPaul Durrant xs_transaction_t tid; 121a783f8adSPaul Durrant char **key; 122a783f8adSPaul Durrant QDict *opts; 123a783f8adSPaul Durrant unsigned int i, n; 124a783f8adSPaul Durrant 125a783f8adSPaul Durrant trace_xen_bus_backend_create(type, path); 126a783f8adSPaul Durrant 127a783f8adSPaul Durrant again: 128ba2a92dbSPaul Durrant tid = qemu_xen_xs_transaction_start(xenbus->xsh); 129a783f8adSPaul Durrant if (tid == XBT_NULL) { 130a783f8adSPaul Durrant error_setg(errp, "failed xs_transaction_start"); 131a783f8adSPaul Durrant return; 132a783f8adSPaul Durrant } 133a783f8adSPaul Durrant 134ba2a92dbSPaul Durrant key = qemu_xen_xs_directory(xenbus->xsh, tid, path, &n); 135a783f8adSPaul Durrant if (!key) { 136ba2a92dbSPaul Durrant if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, true)) { 137a783f8adSPaul Durrant error_setg_errno(errp, errno, "failed xs_transaction_end"); 138a783f8adSPaul Durrant } 139a783f8adSPaul Durrant return; 140a783f8adSPaul Durrant } 141a783f8adSPaul Durrant 142a783f8adSPaul Durrant opts = qdict_new(); 143a783f8adSPaul Durrant for (i = 0; i < n; i++) { 144a783f8adSPaul Durrant char *val; 145a783f8adSPaul Durrant 146a783f8adSPaul Durrant /* 147a783f8adSPaul Durrant * Assume anything found in the xenstore backend area, other than 148a783f8adSPaul Durrant * the keys created for a generic XenDevice, are parameters 149a783f8adSPaul Durrant * to be used to configure the backend. 150a783f8adSPaul Durrant */ 151a783f8adSPaul Durrant if (!strcmp(key[i], "state") || 152a783f8adSPaul Durrant !strcmp(key[i], "online") || 153a783f8adSPaul Durrant !strcmp(key[i], "frontend") || 154a783f8adSPaul Durrant !strcmp(key[i], "frontend-id") || 155a783f8adSPaul Durrant !strcmp(key[i], "hotplug-status")) 156a783f8adSPaul Durrant continue; 157a783f8adSPaul Durrant 158a783f8adSPaul Durrant if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms", 159a783f8adSPaul Durrant &val) == 1) { 160a783f8adSPaul Durrant qdict_put_str(opts, key[i], val); 161a783f8adSPaul Durrant free(val); 162a783f8adSPaul Durrant } 163a783f8adSPaul Durrant } 164a783f8adSPaul Durrant 165a783f8adSPaul Durrant free(key); 166a783f8adSPaul Durrant 167ba2a92dbSPaul Durrant if (!qemu_xen_xs_transaction_end(xenbus->xsh, tid, false)) { 168a783f8adSPaul Durrant qobject_unref(opts); 169a783f8adSPaul Durrant 170a783f8adSPaul Durrant if (errno == EAGAIN) { 171a783f8adSPaul Durrant goto again; 172a783f8adSPaul Durrant } 173a783f8adSPaul Durrant 174a783f8adSPaul Durrant error_setg_errno(errp, errno, "failed xs_transaction_end"); 175a783f8adSPaul Durrant return; 176a783f8adSPaul Durrant } 177a783f8adSPaul Durrant 1781de7096dSVladimir Sementsov-Ogievskiy xen_backend_device_create(xenbus, type, name, opts, errp); 179a783f8adSPaul Durrant qobject_unref(opts); 180a783f8adSPaul Durrant 1811de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 1821de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to create '%s' device '%s': ", type, name); 183a783f8adSPaul Durrant } 184a783f8adSPaul Durrant } 185a783f8adSPaul Durrant 186a783f8adSPaul Durrant static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) 187a783f8adSPaul Durrant { 188a783f8adSPaul Durrant char *domain_path = g_strdup_printf("backend/%s/%u", type, xen_domid); 189a783f8adSPaul Durrant char **backend; 190a783f8adSPaul Durrant unsigned int i, n; 191a783f8adSPaul Durrant 192a783f8adSPaul Durrant trace_xen_bus_type_enumerate(type); 193a783f8adSPaul Durrant 194ba2a92dbSPaul Durrant backend = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); 195a783f8adSPaul Durrant if (!backend) { 196a783f8adSPaul Durrant goto out; 197a783f8adSPaul Durrant } 198a783f8adSPaul Durrant 199a783f8adSPaul Durrant for (i = 0; i < n; i++) { 200a783f8adSPaul Durrant char *backend_path = g_strdup_printf("%s/%s", domain_path, 201a783f8adSPaul Durrant backend[i]); 2023809f758SPaul Durrant enum xenbus_state state; 2033809f758SPaul Durrant unsigned int online; 204a783f8adSPaul Durrant 205a783f8adSPaul Durrant if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "state", 2063809f758SPaul Durrant NULL, "%u", &state) != 1) 2073809f758SPaul Durrant state = XenbusStateUnknown; 208a783f8adSPaul Durrant 2093809f758SPaul Durrant if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "online", 2103809f758SPaul Durrant NULL, "%u", &online) != 1) 2113809f758SPaul Durrant online = 0; 2123809f758SPaul Durrant 213eb6ae7a6SDavid Woodhouse if (online && state == XenbusStateInitialising && 214eb6ae7a6SDavid Woodhouse !xen_backend_exists(type, backend[i])) { 215a783f8adSPaul Durrant Error *local_err = NULL; 216a783f8adSPaul Durrant 217a783f8adSPaul Durrant xen_bus_backend_create(xenbus, type, backend[i], backend_path, 218a783f8adSPaul Durrant &local_err); 219a783f8adSPaul Durrant if (local_err) { 220a783f8adSPaul Durrant error_report_err(local_err); 221a783f8adSPaul Durrant } 222a783f8adSPaul Durrant } 223a783f8adSPaul Durrant 224a783f8adSPaul Durrant g_free(backend_path); 225a783f8adSPaul Durrant } 226a783f8adSPaul Durrant 227a783f8adSPaul Durrant free(backend); 228a783f8adSPaul Durrant 229a783f8adSPaul Durrant out: 230a783f8adSPaul Durrant g_free(domain_path); 231a783f8adSPaul Durrant } 232a783f8adSPaul Durrant 2333809f758SPaul Durrant static void xen_bus_enumerate(XenBus *xenbus) 234a783f8adSPaul Durrant { 235a783f8adSPaul Durrant char **type; 236a783f8adSPaul Durrant unsigned int i, n; 237a783f8adSPaul Durrant 238a783f8adSPaul Durrant trace_xen_bus_enumerate(); 239a783f8adSPaul Durrant 240ba2a92dbSPaul Durrant type = qemu_xen_xs_directory(xenbus->xsh, XBT_NULL, "backend", &n); 241a783f8adSPaul Durrant if (!type) { 242a783f8adSPaul Durrant return; 243a783f8adSPaul Durrant } 244a783f8adSPaul Durrant 245a783f8adSPaul Durrant for (i = 0; i < n; i++) { 246a783f8adSPaul Durrant xen_bus_type_enumerate(xenbus, type[i]); 247a783f8adSPaul Durrant } 248a783f8adSPaul Durrant 249a783f8adSPaul Durrant free(type); 250a783f8adSPaul Durrant } 251a783f8adSPaul Durrant 2523809f758SPaul Durrant static void xen_bus_device_cleanup(XenDevice *xendev) 2533809f758SPaul Durrant { 2543809f758SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 2553809f758SPaul Durrant Error *local_err = NULL; 2563809f758SPaul Durrant 2573809f758SPaul Durrant trace_xen_bus_device_cleanup(type, xendev->name); 2583809f758SPaul Durrant 2593809f758SPaul Durrant g_assert(!xendev->backend_online); 2603809f758SPaul Durrant 2613809f758SPaul Durrant if (!xen_backend_try_device_destroy(xendev, &local_err)) { 2623809f758SPaul Durrant object_unparent(OBJECT(xendev)); 2633809f758SPaul Durrant } 2643809f758SPaul Durrant 2653809f758SPaul Durrant if (local_err) { 2663809f758SPaul Durrant error_report_err(local_err); 2673809f758SPaul Durrant } 2683809f758SPaul Durrant } 2693809f758SPaul Durrant 2703809f758SPaul Durrant static void xen_bus_cleanup(XenBus *xenbus) 2713809f758SPaul Durrant { 2723809f758SPaul Durrant XenDevice *xendev, *next; 2733809f758SPaul Durrant 2743809f758SPaul Durrant trace_xen_bus_cleanup(); 2753809f758SPaul Durrant 2763809f758SPaul Durrant QLIST_FOREACH_SAFE(xendev, &xenbus->inactive_devices, list, next) { 2773809f758SPaul Durrant g_assert(xendev->inactive); 2783809f758SPaul Durrant QLIST_REMOVE(xendev, list); 2793809f758SPaul Durrant xen_bus_device_cleanup(xendev); 2803809f758SPaul Durrant } 2813809f758SPaul Durrant } 2823809f758SPaul Durrant 283ba2a92dbSPaul Durrant static void xen_bus_backend_changed(void *opaque, const char *path) 2843809f758SPaul Durrant { 2853809f758SPaul Durrant XenBus *xenbus = opaque; 2863809f758SPaul Durrant 2873809f758SPaul Durrant xen_bus_enumerate(xenbus); 2883809f758SPaul Durrant xen_bus_cleanup(xenbus); 2893809f758SPaul Durrant } 2903809f758SPaul Durrant 291b69c3c21SMarkus Armbruster static void xen_bus_unrealize(BusState *bus) 292108f7bbaSPaul Durrant { 293094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(bus); 294094a2239SPaul Durrant 295108f7bbaSPaul Durrant trace_xen_bus_unrealize(); 296094a2239SPaul Durrant 297a783f8adSPaul Durrant if (xenbus->backend_watch) { 298c4583c8cSPaul Durrant unsigned int i; 299c4583c8cSPaul Durrant 300c4583c8cSPaul Durrant for (i = 0; i < xenbus->backend_types; i++) { 301c4583c8cSPaul Durrant if (xenbus->backend_watch[i]) { 302ba2a92dbSPaul Durrant xs_node_unwatch(xenbus->xsh, xenbus->backend_watch[i]); 303c4583c8cSPaul Durrant } 304c4583c8cSPaul Durrant } 305c4583c8cSPaul Durrant 306c4583c8cSPaul Durrant g_free(xenbus->backend_watch); 307a783f8adSPaul Durrant xenbus->backend_watch = NULL; 308a783f8adSPaul Durrant } 309a783f8adSPaul Durrant 310374752a2SPaul Durrant if (xenbus->xsh) { 311ba2a92dbSPaul Durrant qemu_xen_xs_close(xenbus->xsh); 312108f7bbaSPaul Durrant } 31382a29e30SPaul Durrant } 31482a29e30SPaul Durrant 315108f7bbaSPaul Durrant static void xen_bus_realize(BusState *bus, Error **errp) 316108f7bbaSPaul Durrant { 317c4583c8cSPaul Durrant char *key = g_strdup_printf("%u", xen_domid); 318094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(bus); 319094a2239SPaul Durrant unsigned int domid; 320c4583c8cSPaul Durrant const char **type; 321c4583c8cSPaul Durrant unsigned int i; 322a783f8adSPaul Durrant Error *local_err = NULL; 323094a2239SPaul Durrant 324108f7bbaSPaul Durrant trace_xen_bus_realize(); 325094a2239SPaul Durrant 326ba2a92dbSPaul Durrant xenbus->xsh = qemu_xen_xs_open(); 327094a2239SPaul Durrant if (!xenbus->xsh) { 328094a2239SPaul Durrant error_setg_errno(errp, errno, "failed xs_open"); 329094a2239SPaul Durrant goto fail; 330094a2239SPaul Durrant } 331094a2239SPaul Durrant 332094a2239SPaul Durrant if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */ 333094a2239SPaul Durrant "domid", NULL, "%u", &domid) == 1) { 334094a2239SPaul Durrant xenbus->backend_id = domid; 335094a2239SPaul Durrant } else { 336094a2239SPaul Durrant xenbus->backend_id = 0; /* Assume lack of node means dom0 */ 337094a2239SPaul Durrant } 338094a2239SPaul Durrant 339a783f8adSPaul Durrant module_call_init(MODULE_INIT_XEN_BACKEND); 340a783f8adSPaul Durrant 341c4583c8cSPaul Durrant type = xen_backend_get_types(&xenbus->backend_types); 342ba2a92dbSPaul Durrant xenbus->backend_watch = g_new(struct qemu_xs_watch *, 343ba2a92dbSPaul Durrant xenbus->backend_types); 344c4583c8cSPaul Durrant 345c4583c8cSPaul Durrant for (i = 0; i < xenbus->backend_types; i++) { 346c4583c8cSPaul Durrant char *node = g_strdup_printf("backend/%s", type[i]); 347c4583c8cSPaul Durrant 348c4583c8cSPaul Durrant xenbus->backend_watch[i] = 349ba2a92dbSPaul Durrant xs_node_watch(xenbus->xsh, node, key, xen_bus_backend_changed, 350ba2a92dbSPaul Durrant xenbus, &local_err); 351a783f8adSPaul Durrant if (local_err) { 352a783f8adSPaul Durrant /* This need not be treated as a hard error so don't propagate */ 353a783f8adSPaul Durrant error_reportf_err(local_err, 354c4583c8cSPaul Durrant "failed to set up '%s' enumeration watch: ", 355c4583c8cSPaul Durrant type[i]); 356a783f8adSPaul Durrant } 357a783f8adSPaul Durrant 358c4583c8cSPaul Durrant g_free(node); 359c4583c8cSPaul Durrant } 360c4583c8cSPaul Durrant 361c4583c8cSPaul Durrant g_free(type); 362c4583c8cSPaul Durrant g_free(key); 363094a2239SPaul Durrant return; 364094a2239SPaul Durrant 365094a2239SPaul Durrant fail: 366b69c3c21SMarkus Armbruster xen_bus_unrealize(bus); 367c4583c8cSPaul Durrant g_free(key); 368108f7bbaSPaul Durrant } 369108f7bbaSPaul Durrant 370b6af8926SPaul Durrant static void xen_bus_unplug_request(HotplugHandler *hotplug, 371b6af8926SPaul Durrant DeviceState *dev, 372b6af8926SPaul Durrant Error **errp) 373b6af8926SPaul Durrant { 374b6af8926SPaul Durrant XenDevice *xendev = XEN_DEVICE(dev); 375b6af8926SPaul Durrant 376b6af8926SPaul Durrant xen_device_unplug(xendev, errp); 377b6af8926SPaul Durrant } 378b6af8926SPaul Durrant 379108f7bbaSPaul Durrant static void xen_bus_class_init(ObjectClass *class, void *data) 380108f7bbaSPaul Durrant { 381108f7bbaSPaul Durrant BusClass *bus_class = BUS_CLASS(class); 382b6af8926SPaul Durrant HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class); 383108f7bbaSPaul Durrant 384094a2239SPaul Durrant bus_class->print_dev = xen_bus_print_dev; 385094a2239SPaul Durrant bus_class->get_dev_path = xen_bus_get_dev_path; 386108f7bbaSPaul Durrant bus_class->realize = xen_bus_realize; 387108f7bbaSPaul Durrant bus_class->unrealize = xen_bus_unrealize; 388b6af8926SPaul Durrant 389b6af8926SPaul Durrant hotplug_class->unplug_request = xen_bus_unplug_request; 390108f7bbaSPaul Durrant } 391108f7bbaSPaul Durrant 392108f7bbaSPaul Durrant static const TypeInfo xen_bus_type_info = { 393108f7bbaSPaul Durrant .name = TYPE_XEN_BUS, 394108f7bbaSPaul Durrant .parent = TYPE_BUS, 395108f7bbaSPaul Durrant .instance_size = sizeof(XenBus), 396108f7bbaSPaul Durrant .class_size = sizeof(XenBusClass), 397108f7bbaSPaul Durrant .class_init = xen_bus_class_init, 398108f7bbaSPaul Durrant .interfaces = (InterfaceInfo[]) { 399108f7bbaSPaul Durrant { TYPE_HOTPLUG_HANDLER }, 400108f7bbaSPaul Durrant { } 401108f7bbaSPaul Durrant }, 402108f7bbaSPaul Durrant }; 403108f7bbaSPaul Durrant 404b6af8926SPaul Durrant void xen_device_backend_printf(XenDevice *xendev, const char *key, 405094a2239SPaul Durrant const char *fmt, ...) 406094a2239SPaul Durrant { 407094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 408094a2239SPaul Durrant Error *local_err = NULL; 409094a2239SPaul Durrant va_list ap; 410094a2239SPaul Durrant 411094a2239SPaul Durrant g_assert(xenbus->xsh); 412094a2239SPaul Durrant 413094a2239SPaul Durrant va_start(ap, fmt); 414094a2239SPaul Durrant xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, 415094a2239SPaul Durrant &local_err, fmt, ap); 416094a2239SPaul Durrant va_end(ap); 417094a2239SPaul Durrant 418094a2239SPaul Durrant if (local_err) { 419094a2239SPaul Durrant error_report_err(local_err); 420094a2239SPaul Durrant } 421094a2239SPaul Durrant } 422094a2239SPaul Durrant 423d62449daSDaniel P. Berrangé G_GNUC_SCANF(3, 4) 42482a29e30SPaul Durrant static int xen_device_backend_scanf(XenDevice *xendev, const char *key, 42582a29e30SPaul Durrant const char *fmt, ...) 42682a29e30SPaul Durrant { 42782a29e30SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 42882a29e30SPaul Durrant va_list ap; 42982a29e30SPaul Durrant int rc; 43082a29e30SPaul Durrant 43182a29e30SPaul Durrant g_assert(xenbus->xsh); 43282a29e30SPaul Durrant 43382a29e30SPaul Durrant va_start(ap, fmt); 43482a29e30SPaul Durrant rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, 43582a29e30SPaul Durrant NULL, fmt, ap); 43682a29e30SPaul Durrant va_end(ap); 43782a29e30SPaul Durrant 43882a29e30SPaul Durrant return rc; 43982a29e30SPaul Durrant } 44082a29e30SPaul Durrant 44182a29e30SPaul Durrant void xen_device_backend_set_state(XenDevice *xendev, 442094a2239SPaul Durrant enum xenbus_state state) 443094a2239SPaul Durrant { 444094a2239SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 445094a2239SPaul Durrant 446094a2239SPaul Durrant if (xendev->backend_state == state) { 447094a2239SPaul Durrant return; 448094a2239SPaul Durrant } 449094a2239SPaul Durrant 450094a2239SPaul Durrant trace_xen_device_backend_state(type, xendev->name, 451094a2239SPaul Durrant xs_strstate(state)); 452094a2239SPaul Durrant 453094a2239SPaul Durrant xendev->backend_state = state; 454094a2239SPaul Durrant xen_device_backend_printf(xendev, "state", "%u", state); 455094a2239SPaul Durrant } 456094a2239SPaul Durrant 45782a29e30SPaul Durrant enum xenbus_state xen_device_backend_get_state(XenDevice *xendev) 45882a29e30SPaul Durrant { 45982a29e30SPaul Durrant return xendev->backend_state; 46082a29e30SPaul Durrant } 46182a29e30SPaul Durrant 462b6af8926SPaul Durrant static void xen_device_backend_set_online(XenDevice *xendev, bool online) 463b6af8926SPaul Durrant { 464b6af8926SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 465b6af8926SPaul Durrant 466b6af8926SPaul Durrant if (xendev->backend_online == online) { 467b6af8926SPaul Durrant return; 468b6af8926SPaul Durrant } 469b6af8926SPaul Durrant 470b6af8926SPaul Durrant trace_xen_device_backend_online(type, xendev->name, online); 471b6af8926SPaul Durrant 472b6af8926SPaul Durrant xendev->backend_online = online; 473b6af8926SPaul Durrant xen_device_backend_printf(xendev, "online", "%u", online); 474b6af8926SPaul Durrant } 475b6af8926SPaul Durrant 476cb323146SAnthony PERARD /* 477cb323146SAnthony PERARD * Tell from the state whether the frontend is likely alive, 478cb323146SAnthony PERARD * i.e. it will react to a change of state of the backend. 479cb323146SAnthony PERARD */ 4803809f758SPaul Durrant static bool xen_device_frontend_is_active(XenDevice *xendev) 481cb323146SAnthony PERARD { 4823809f758SPaul Durrant switch (xendev->frontend_state) { 483cb323146SAnthony PERARD case XenbusStateInitWait: 484cb323146SAnthony PERARD case XenbusStateInitialised: 485cb323146SAnthony PERARD case XenbusStateConnected: 486cb323146SAnthony PERARD case XenbusStateClosing: 487cb323146SAnthony PERARD return true; 488cb323146SAnthony PERARD default: 489cb323146SAnthony PERARD return false; 490cb323146SAnthony PERARD } 491cb323146SAnthony PERARD } 492cb323146SAnthony PERARD 493ba2a92dbSPaul Durrant static void xen_device_backend_changed(void *opaque, const char *path) 494b6af8926SPaul Durrant { 495b6af8926SPaul Durrant XenDevice *xendev = opaque; 496b6af8926SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 497b6af8926SPaul Durrant enum xenbus_state state; 498b6af8926SPaul Durrant unsigned int online; 499b6af8926SPaul Durrant 500b6af8926SPaul Durrant trace_xen_device_backend_changed(type, xendev->name); 501b6af8926SPaul Durrant 502b6af8926SPaul Durrant if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) { 503b6af8926SPaul Durrant state = XenbusStateUnknown; 504b6af8926SPaul Durrant } 505b6af8926SPaul Durrant 506b6af8926SPaul Durrant xen_device_backend_set_state(xendev, state); 507b6af8926SPaul Durrant 508b6af8926SPaul Durrant if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) { 509b6af8926SPaul Durrant online = 0; 510b6af8926SPaul Durrant } 511b6af8926SPaul Durrant 512b6af8926SPaul Durrant xen_device_backend_set_online(xendev, !!online); 513b6af8926SPaul Durrant 514b6af8926SPaul Durrant /* 515b6af8926SPaul Durrant * If the toolstack (or unplug request callback) has set the backend 516cb323146SAnthony PERARD * state to Closing, but there is no active frontend then set the 517cb323146SAnthony PERARD * backend state to Closed. 518b6af8926SPaul Durrant */ 5193809f758SPaul Durrant if (state == XenbusStateClosing && 5203809f758SPaul Durrant !xen_device_frontend_is_active(xendev)) { 521b6af8926SPaul Durrant xen_device_backend_set_state(xendev, XenbusStateClosed); 522b6af8926SPaul Durrant } 523b6af8926SPaul Durrant 524b6af8926SPaul Durrant /* 52567bc8e00SPaul Durrant * If a backend is still 'online' then we should leave it alone but, 5263809f758SPaul Durrant * if a backend is not 'online', then the device is a candidate 5273809f758SPaul Durrant * for destruction. Hence add it to the 'inactive' list to be cleaned 5283809f758SPaul Durrant * by xen_bus_cleanup(). 529b6af8926SPaul Durrant */ 5303809f758SPaul Durrant if (!online && 5313809f758SPaul Durrant (state == XenbusStateClosed || state == XenbusStateInitialising || 5323809f758SPaul Durrant state == XenbusStateInitWait || state == XenbusStateUnknown) && 5333809f758SPaul Durrant !xendev->inactive) { 5343809f758SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 535a783f8adSPaul Durrant 5363809f758SPaul Durrant xendev->inactive = true; 5373809f758SPaul Durrant QLIST_INSERT_HEAD(&xenbus->inactive_devices, xendev, list); 538a783f8adSPaul Durrant 5393809f758SPaul Durrant /* 5403809f758SPaul Durrant * Re-write the state to cause a XenBus backend_watch notification, 5413809f758SPaul Durrant * resulting in a call to xen_bus_cleanup(). 5423809f758SPaul Durrant */ 5433809f758SPaul Durrant xen_device_backend_printf(xendev, "state", "%u", state); 544a783f8adSPaul Durrant } 545b6af8926SPaul Durrant } 546b6af8926SPaul Durrant 547094a2239SPaul Durrant static void xen_device_backend_create(XenDevice *xendev, Error **errp) 548094a2239SPaul Durrant { 5491de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 550094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 551094a2239SPaul Durrant 552094a2239SPaul Durrant xendev->backend_path = xen_device_get_backend_path(xendev); 553094a2239SPaul Durrant 554094a2239SPaul Durrant g_assert(xenbus->xsh); 555094a2239SPaul Durrant 556ba2a92dbSPaul Durrant xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, 557ba2a92dbSPaul Durrant xenbus->backend_id, xendev->frontend_id, XS_PERM_READ, errp); 5581de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 5591de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to create backend: "); 560b6af8926SPaul Durrant return; 561b6af8926SPaul Durrant } 562b6af8926SPaul Durrant 563b6af8926SPaul Durrant xendev->backend_state_watch = 564ba2a92dbSPaul Durrant xs_node_watch(xendev->xsh, xendev->backend_path, 565ba2a92dbSPaul Durrant "state", xen_device_backend_changed, xendev, 5661de7096dSVladimir Sementsov-Ogievskiy errp); 5671de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 5681de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to watch backend state: "); 569b6af8926SPaul Durrant return; 570b6af8926SPaul Durrant } 571b6af8926SPaul Durrant 572b6af8926SPaul Durrant xendev->backend_online_watch = 573ba2a92dbSPaul Durrant xs_node_watch(xendev->xsh, xendev->backend_path, 574ba2a92dbSPaul Durrant "online", xen_device_backend_changed, xendev, 5751de7096dSVladimir Sementsov-Ogievskiy errp); 5761de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 5771de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to watch backend online: "); 578b6af8926SPaul Durrant return; 579094a2239SPaul Durrant } 580094a2239SPaul Durrant } 581094a2239SPaul Durrant 582094a2239SPaul Durrant static void xen_device_backend_destroy(XenDevice *xendev) 583094a2239SPaul Durrant { 584094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 585094a2239SPaul Durrant Error *local_err = NULL; 586094a2239SPaul Durrant 587b6af8926SPaul Durrant if (xendev->backend_online_watch) { 588ba2a92dbSPaul Durrant xs_node_unwatch(xendev->xsh, xendev->backend_online_watch); 589b6af8926SPaul Durrant xendev->backend_online_watch = NULL; 590b6af8926SPaul Durrant } 591b6af8926SPaul Durrant 592b6af8926SPaul Durrant if (xendev->backend_state_watch) { 593ba2a92dbSPaul Durrant xs_node_unwatch(xendev->xsh, xendev->backend_state_watch); 594b6af8926SPaul Durrant xendev->backend_state_watch = NULL; 595b6af8926SPaul Durrant } 596b6af8926SPaul Durrant 597094a2239SPaul Durrant if (!xendev->backend_path) { 598094a2239SPaul Durrant return; 599094a2239SPaul Durrant } 600094a2239SPaul Durrant 601094a2239SPaul Durrant g_assert(xenbus->xsh); 602094a2239SPaul Durrant 603094a2239SPaul Durrant xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->backend_path, 604094a2239SPaul Durrant &local_err); 605094a2239SPaul Durrant g_free(xendev->backend_path); 606094a2239SPaul Durrant xendev->backend_path = NULL; 607094a2239SPaul Durrant 608094a2239SPaul Durrant if (local_err) { 609094a2239SPaul Durrant error_report_err(local_err); 610094a2239SPaul Durrant } 611094a2239SPaul Durrant } 612094a2239SPaul Durrant 613b6af8926SPaul Durrant void xen_device_frontend_printf(XenDevice *xendev, const char *key, 614094a2239SPaul Durrant const char *fmt, ...) 615094a2239SPaul Durrant { 616094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 617094a2239SPaul Durrant Error *local_err = NULL; 618094a2239SPaul Durrant va_list ap; 619094a2239SPaul Durrant 620094a2239SPaul Durrant g_assert(xenbus->xsh); 621094a2239SPaul Durrant 622094a2239SPaul Durrant va_start(ap, fmt); 623094a2239SPaul Durrant xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, 624094a2239SPaul Durrant &local_err, fmt, ap); 625094a2239SPaul Durrant va_end(ap); 626094a2239SPaul Durrant 627094a2239SPaul Durrant if (local_err) { 628094a2239SPaul Durrant error_report_err(local_err); 629094a2239SPaul Durrant } 630094a2239SPaul Durrant } 631094a2239SPaul Durrant 632b6af8926SPaul Durrant int xen_device_frontend_scanf(XenDevice *xendev, const char *key, 63382a29e30SPaul Durrant const char *fmt, ...) 63482a29e30SPaul Durrant { 63582a29e30SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 63682a29e30SPaul Durrant va_list ap; 63782a29e30SPaul Durrant int rc; 63882a29e30SPaul Durrant 63982a29e30SPaul Durrant g_assert(xenbus->xsh); 64082a29e30SPaul Durrant 64182a29e30SPaul Durrant va_start(ap, fmt); 64282a29e30SPaul Durrant rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, 64382a29e30SPaul Durrant NULL, fmt, ap); 64482a29e30SPaul Durrant va_end(ap); 64582a29e30SPaul Durrant 64682a29e30SPaul Durrant return rc; 64782a29e30SPaul Durrant } 64882a29e30SPaul Durrant 649094a2239SPaul Durrant static void xen_device_frontend_set_state(XenDevice *xendev, 650705be570SAnthony PERARD enum xenbus_state state, 651705be570SAnthony PERARD bool publish) 652094a2239SPaul Durrant { 653094a2239SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 654094a2239SPaul Durrant 655094a2239SPaul Durrant if (xendev->frontend_state == state) { 656094a2239SPaul Durrant return; 657094a2239SPaul Durrant } 658094a2239SPaul Durrant 659094a2239SPaul Durrant trace_xen_device_frontend_state(type, xendev->name, 660094a2239SPaul Durrant xs_strstate(state)); 661094a2239SPaul Durrant 662094a2239SPaul Durrant xendev->frontend_state = state; 663705be570SAnthony PERARD if (publish) { 664094a2239SPaul Durrant xen_device_frontend_printf(xendev, "state", "%u", state); 665094a2239SPaul Durrant } 666705be570SAnthony PERARD } 667094a2239SPaul Durrant 668ba2a92dbSPaul Durrant static void xen_device_frontend_changed(void *opaque, const char *path) 66982a29e30SPaul Durrant { 67082a29e30SPaul Durrant XenDevice *xendev = opaque; 67182a29e30SPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 67282a29e30SPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 67382a29e30SPaul Durrant enum xenbus_state state; 67482a29e30SPaul Durrant 67582a29e30SPaul Durrant trace_xen_device_frontend_changed(type, xendev->name); 67682a29e30SPaul Durrant 67782a29e30SPaul Durrant if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) { 67882a29e30SPaul Durrant state = XenbusStateUnknown; 67982a29e30SPaul Durrant } 68082a29e30SPaul Durrant 681705be570SAnthony PERARD xen_device_frontend_set_state(xendev, state, false); 68282a29e30SPaul Durrant 68367bc8e00SPaul Durrant if (state == XenbusStateInitialising && 68467bc8e00SPaul Durrant xendev->backend_state == XenbusStateClosed && 68567bc8e00SPaul Durrant xendev->backend_online) { 68667bc8e00SPaul Durrant /* 68767bc8e00SPaul Durrant * The frontend is re-initializing so switch back to 68867bc8e00SPaul Durrant * InitWait. 68967bc8e00SPaul Durrant */ 69067bc8e00SPaul Durrant xen_device_backend_set_state(xendev, XenbusStateInitWait); 69167bc8e00SPaul Durrant return; 69267bc8e00SPaul Durrant } 69367bc8e00SPaul Durrant 69482a29e30SPaul Durrant if (xendev_class->frontend_changed) { 69582a29e30SPaul Durrant Error *local_err = NULL; 69682a29e30SPaul Durrant 69782a29e30SPaul Durrant xendev_class->frontend_changed(xendev, state, &local_err); 69882a29e30SPaul Durrant 69982a29e30SPaul Durrant if (local_err) { 70082a29e30SPaul Durrant error_reportf_err(local_err, "frontend change error: "); 70182a29e30SPaul Durrant } 70282a29e30SPaul Durrant } 70382a29e30SPaul Durrant } 70482a29e30SPaul Durrant 7056bd6b955SMark Syms static bool xen_device_frontend_exists(XenDevice *xendev) 7066bd6b955SMark Syms { 7076bd6b955SMark Syms enum xenbus_state state; 7086bd6b955SMark Syms 7096bd6b955SMark Syms return (xen_device_frontend_scanf(xendev, "state", "%u", &state) == 1); 7106bd6b955SMark Syms } 7116bd6b955SMark Syms 712094a2239SPaul Durrant static void xen_device_frontend_create(XenDevice *xendev, Error **errp) 713094a2239SPaul Durrant { 7141de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 715094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 716523b6b3aSDavid Woodhouse XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 717094a2239SPaul Durrant 718523b6b3aSDavid Woodhouse if (xendev_class->get_frontend_path) { 719523b6b3aSDavid Woodhouse xendev->frontend_path = xendev_class->get_frontend_path(xendev, errp); 720523b6b3aSDavid Woodhouse if (!xendev->frontend_path) { 721523b6b3aSDavid Woodhouse error_prepend(errp, "failed to create frontend: "); 722523b6b3aSDavid Woodhouse return; 723523b6b3aSDavid Woodhouse } 724523b6b3aSDavid Woodhouse } else { 725094a2239SPaul Durrant xendev->frontend_path = xen_device_get_frontend_path(xendev); 726523b6b3aSDavid Woodhouse } 727094a2239SPaul Durrant 7286bd6b955SMark Syms /* 7296bd6b955SMark Syms * The frontend area may have already been created by a legacy 7306bd6b955SMark Syms * toolstack. 7316bd6b955SMark Syms */ 7326bd6b955SMark Syms if (!xen_device_frontend_exists(xendev)) { 733094a2239SPaul Durrant g_assert(xenbus->xsh); 734094a2239SPaul Durrant 735ba2a92dbSPaul Durrant xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, 736ba2a92dbSPaul Durrant xendev->frontend_id, xenbus->backend_id, 737ba2a92dbSPaul Durrant XS_PERM_READ | XS_PERM_WRITE, errp); 7381de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 7391de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to create frontend: "); 74082a29e30SPaul Durrant return; 74182a29e30SPaul Durrant } 7426bd6b955SMark Syms } 74382a29e30SPaul Durrant 74482a29e30SPaul Durrant xendev->frontend_state_watch = 745ba2a92dbSPaul Durrant xs_node_watch(xendev->xsh, xendev->frontend_path, "state", 746ba2a92dbSPaul Durrant xen_device_frontend_changed, xendev, errp); 7471de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 7481de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to watch frontend state: "); 749094a2239SPaul Durrant } 750094a2239SPaul Durrant } 751094a2239SPaul Durrant 752094a2239SPaul Durrant static void xen_device_frontend_destroy(XenDevice *xendev) 753094a2239SPaul Durrant { 754094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 755094a2239SPaul Durrant Error *local_err = NULL; 756094a2239SPaul Durrant 75782a29e30SPaul Durrant if (xendev->frontend_state_watch) { 758ba2a92dbSPaul Durrant xs_node_unwatch(xendev->xsh, xendev->frontend_state_watch); 75982a29e30SPaul Durrant xendev->frontend_state_watch = NULL; 76082a29e30SPaul Durrant } 76182a29e30SPaul Durrant 762094a2239SPaul Durrant if (!xendev->frontend_path) { 763094a2239SPaul Durrant return; 764094a2239SPaul Durrant } 765094a2239SPaul Durrant 766094a2239SPaul Durrant g_assert(xenbus->xsh); 767094a2239SPaul Durrant 768094a2239SPaul Durrant xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->frontend_path, 769094a2239SPaul Durrant &local_err); 770094a2239SPaul Durrant g_free(xendev->frontend_path); 771094a2239SPaul Durrant xendev->frontend_path = NULL; 772094a2239SPaul Durrant 773094a2239SPaul Durrant if (local_err) { 774094a2239SPaul Durrant error_report_err(local_err); 775094a2239SPaul Durrant } 776094a2239SPaul Durrant } 777094a2239SPaul Durrant 7784b34b5b1SPaul Durrant void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, 7794b34b5b1SPaul Durrant Error **errp) 7804b34b5b1SPaul Durrant { 781c412ba47SDavid Woodhouse if (qemu_xen_gnttab_set_max_grants(xendev->xgth, nr_refs)) { 7824b34b5b1SPaul Durrant error_setg_errno(errp, errno, "xengnttab_set_max_grants failed"); 7834b34b5b1SPaul Durrant } 7844b34b5b1SPaul Durrant } 7854b34b5b1SPaul Durrant 7864b34b5b1SPaul Durrant void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, 7874b34b5b1SPaul Durrant unsigned int nr_refs, int prot, 7884b34b5b1SPaul Durrant Error **errp) 7894b34b5b1SPaul Durrant { 790c412ba47SDavid Woodhouse void *map = qemu_xen_gnttab_map_refs(xendev->xgth, nr_refs, 791c412ba47SDavid Woodhouse xendev->frontend_id, refs, prot); 7924b34b5b1SPaul Durrant 7934b34b5b1SPaul Durrant if (!map) { 7944b34b5b1SPaul Durrant error_setg_errno(errp, errno, 7954b34b5b1SPaul Durrant "xengnttab_map_domain_grant_refs failed"); 7964b34b5b1SPaul Durrant } 7974b34b5b1SPaul Durrant 7984b34b5b1SPaul Durrant return map; 7994b34b5b1SPaul Durrant } 8004b34b5b1SPaul Durrant 801f80fad16SDavid Woodhouse void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, uint32_t *refs, 8024b34b5b1SPaul Durrant unsigned int nr_refs, Error **errp) 8034b34b5b1SPaul Durrant { 804f80fad16SDavid Woodhouse if (qemu_xen_gnttab_unmap(xendev->xgth, map, refs, nr_refs)) { 8054b34b5b1SPaul Durrant error_setg_errno(errp, errno, "xengnttab_unmap failed"); 8064b34b5b1SPaul Durrant } 8074b34b5b1SPaul Durrant } 8084b34b5b1SPaul Durrant 8094b34b5b1SPaul Durrant void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, 8104b34b5b1SPaul Durrant XenDeviceGrantCopySegment segs[], 8114b34b5b1SPaul Durrant unsigned int nr_segs, Error **errp) 8124b34b5b1SPaul Durrant { 813c412ba47SDavid Woodhouse qemu_xen_gnttab_grant_copy(xendev->xgth, to_domain, xendev->frontend_id, 814c412ba47SDavid Woodhouse (XenGrantCopySegment *)segs, nr_segs, errp); 8154b34b5b1SPaul Durrant } 8164b34b5b1SPaul Durrant 817a3d669c8SPaul Durrant struct XenEventChannel { 818c0b336eaSPaul Durrant QLIST_ENTRY(XenEventChannel) list; 81983361a8aSPaul Durrant AioContext *ctx; 820c0b336eaSPaul Durrant xenevtchn_handle *xeh; 821a3d669c8SPaul Durrant evtchn_port_t local_port; 822a3d669c8SPaul Durrant XenEventHandler handler; 823a3d669c8SPaul Durrant void *opaque; 824a3d669c8SPaul Durrant }; 825a3d669c8SPaul Durrant 826345f42b4SPaul Durrant static bool xen_device_poll(void *opaque) 827345f42b4SPaul Durrant { 828345f42b4SPaul Durrant XenEventChannel *channel = opaque; 829345f42b4SPaul Durrant 830345f42b4SPaul Durrant return channel->handler(channel->opaque); 831345f42b4SPaul Durrant } 832345f42b4SPaul Durrant 833c0b336eaSPaul Durrant static void xen_device_event(void *opaque) 834a3d669c8SPaul Durrant { 835c0b336eaSPaul Durrant XenEventChannel *channel = opaque; 836b6cacfeaSDavid Woodhouse unsigned long port = qemu_xen_evtchn_pending(channel->xeh); 837a3d669c8SPaul Durrant 838a3d669c8SPaul Durrant if (port == channel->local_port) { 839345f42b4SPaul Durrant xen_device_poll(channel); 840c0b336eaSPaul Durrant 841b6cacfeaSDavid Woodhouse qemu_xen_evtchn_unmask(channel->xeh, port); 842a3d669c8SPaul Durrant } 843a3d669c8SPaul Durrant } 844a3d669c8SPaul Durrant 84532d0b7beSPaul Durrant void xen_device_set_event_channel_context(XenDevice *xendev, 84632d0b7beSPaul Durrant XenEventChannel *channel, 84783361a8aSPaul Durrant AioContext *ctx, 84832d0b7beSPaul Durrant Error **errp) 84932d0b7beSPaul Durrant { 85032d0b7beSPaul Durrant if (!channel) { 85132d0b7beSPaul Durrant error_setg(errp, "bad channel"); 85232d0b7beSPaul Durrant return; 85332d0b7beSPaul Durrant } 85432d0b7beSPaul Durrant 85532d0b7beSPaul Durrant if (channel->ctx) 85660f782b6SStefan Hajnoczi aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), 857826cc324SStefan Hajnoczi NULL, NULL, NULL, NULL, NULL); 85832d0b7beSPaul Durrant 85932d0b7beSPaul Durrant channel->ctx = ctx; 860f6eac904SStefan Hajnoczi if (ctx) { 861f6eac904SStefan Hajnoczi aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), 86260f782b6SStefan Hajnoczi xen_device_event, NULL, xen_device_poll, NULL, 86360f782b6SStefan Hajnoczi channel); 864f6eac904SStefan Hajnoczi } 86532d0b7beSPaul Durrant } 86632d0b7beSPaul Durrant 86732d0b7beSPaul Durrant XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, 868a3d669c8SPaul Durrant unsigned int port, 869a3d669c8SPaul Durrant XenEventHandler handler, 870a3d669c8SPaul Durrant void *opaque, Error **errp) 871a3d669c8SPaul Durrant { 872a3d669c8SPaul Durrant XenEventChannel *channel = g_new0(XenEventChannel, 1); 873a3d669c8SPaul Durrant xenevtchn_port_or_error_t local_port; 874a3d669c8SPaul Durrant 875b6cacfeaSDavid Woodhouse channel->xeh = qemu_xen_evtchn_open(); 876c0b336eaSPaul Durrant if (!channel->xeh) { 877c0b336eaSPaul Durrant error_setg_errno(errp, errno, "failed xenevtchn_open"); 878c0b336eaSPaul Durrant goto fail; 879c0b336eaSPaul Durrant } 880c0b336eaSPaul Durrant 881b6cacfeaSDavid Woodhouse local_port = qemu_xen_evtchn_bind_interdomain(channel->xeh, 882a3d669c8SPaul Durrant xendev->frontend_id, 883a3d669c8SPaul Durrant port); 884a3d669c8SPaul Durrant if (local_port < 0) { 885a3d669c8SPaul Durrant error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed"); 886c0b336eaSPaul Durrant goto fail; 887a3d669c8SPaul Durrant } 888a3d669c8SPaul Durrant 889a3d669c8SPaul Durrant channel->local_port = local_port; 890a3d669c8SPaul Durrant channel->handler = handler; 891a3d669c8SPaul Durrant channel->opaque = opaque; 892a3d669c8SPaul Durrant 89332d0b7beSPaul Durrant /* Only reason for failure is a NULL channel */ 89432d0b7beSPaul Durrant xen_device_set_event_channel_context(xendev, channel, 89532d0b7beSPaul Durrant qemu_get_aio_context(), 89632d0b7beSPaul Durrant &error_abort); 897c0b336eaSPaul Durrant 898c0b336eaSPaul Durrant QLIST_INSERT_HEAD(&xendev->event_channels, channel, list); 899a3d669c8SPaul Durrant 900a3d669c8SPaul Durrant return channel; 901c0b336eaSPaul Durrant 902c0b336eaSPaul Durrant fail: 903c0b336eaSPaul Durrant if (channel->xeh) { 904b6cacfeaSDavid Woodhouse qemu_xen_evtchn_close(channel->xeh); 905c0b336eaSPaul Durrant } 906c0b336eaSPaul Durrant 907c0b336eaSPaul Durrant g_free(channel); 908c0b336eaSPaul Durrant 909c0b336eaSPaul Durrant return NULL; 910a3d669c8SPaul Durrant } 911a3d669c8SPaul Durrant 912a3d669c8SPaul Durrant void xen_device_notify_event_channel(XenDevice *xendev, 913a3d669c8SPaul Durrant XenEventChannel *channel, 914a3d669c8SPaul Durrant Error **errp) 915a3d669c8SPaul Durrant { 916a3d669c8SPaul Durrant if (!channel) { 917a3d669c8SPaul Durrant error_setg(errp, "bad channel"); 918a3d669c8SPaul Durrant return; 919a3d669c8SPaul Durrant } 920a3d669c8SPaul Durrant 921b6cacfeaSDavid Woodhouse if (qemu_xen_evtchn_notify(channel->xeh, channel->local_port) < 0) { 922a3d669c8SPaul Durrant error_setg_errno(errp, errno, "xenevtchn_notify failed"); 923a3d669c8SPaul Durrant } 924a3d669c8SPaul Durrant } 925a3d669c8SPaul Durrant 926a72ccc7fSDavid Woodhouse unsigned int xen_event_channel_get_local_port(XenEventChannel *channel) 927a72ccc7fSDavid Woodhouse { 928a72ccc7fSDavid Woodhouse return channel->local_port; 929a72ccc7fSDavid Woodhouse } 930a72ccc7fSDavid Woodhouse 931a3d669c8SPaul Durrant void xen_device_unbind_event_channel(XenDevice *xendev, 932a3d669c8SPaul Durrant XenEventChannel *channel, 933a3d669c8SPaul Durrant Error **errp) 934a3d669c8SPaul Durrant { 935a3d669c8SPaul Durrant if (!channel) { 936a3d669c8SPaul Durrant error_setg(errp, "bad channel"); 937a3d669c8SPaul Durrant return; 938a3d669c8SPaul Durrant } 939a3d669c8SPaul Durrant 940c0b336eaSPaul Durrant QLIST_REMOVE(channel, list); 941a3d669c8SPaul Durrant 94290006660SAnthony PERARD if (channel->ctx) { 94360f782b6SStefan Hajnoczi aio_set_fd_handler(channel->ctx, qemu_xen_evtchn_fd(channel->xeh), 944826cc324SStefan Hajnoczi NULL, NULL, NULL, NULL, NULL); 94590006660SAnthony PERARD } 946c0b336eaSPaul Durrant 947b6cacfeaSDavid Woodhouse if (qemu_xen_evtchn_unbind(channel->xeh, channel->local_port) < 0) { 948a3d669c8SPaul Durrant error_setg_errno(errp, errno, "xenevtchn_unbind failed"); 949a3d669c8SPaul Durrant } 950a3d669c8SPaul Durrant 951b6cacfeaSDavid Woodhouse qemu_xen_evtchn_close(channel->xeh); 952a3d669c8SPaul Durrant g_free(channel); 953a3d669c8SPaul Durrant } 954a3d669c8SPaul Durrant 955b69c3c21SMarkus Armbruster static void xen_device_unrealize(DeviceState *dev) 956108f7bbaSPaul Durrant { 957108f7bbaSPaul Durrant XenDevice *xendev = XEN_DEVICE(dev); 958108f7bbaSPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 959108f7bbaSPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 960c0b336eaSPaul Durrant XenEventChannel *channel, *next; 961108f7bbaSPaul Durrant 962094a2239SPaul Durrant if (!xendev->name) { 963094a2239SPaul Durrant return; 964094a2239SPaul Durrant } 965094a2239SPaul Durrant 966094a2239SPaul Durrant trace_xen_device_unrealize(type, xendev->name); 967094a2239SPaul Durrant 968094a2239SPaul Durrant if (xendev->exit.notify) { 969094a2239SPaul Durrant qemu_remove_exit_notifier(&xendev->exit); 970094a2239SPaul Durrant xendev->exit.notify = NULL; 971094a2239SPaul Durrant } 972108f7bbaSPaul Durrant 973108f7bbaSPaul Durrant if (xendev_class->unrealize) { 974b69c3c21SMarkus Armbruster xendev_class->unrealize(xendev); 975108f7bbaSPaul Durrant } 976094a2239SPaul Durrant 977c0b336eaSPaul Durrant /* Make sure all event channels are cleaned up */ 978c0b336eaSPaul Durrant QLIST_FOREACH_SAFE(channel, &xendev->event_channels, list, next) { 979c0b336eaSPaul Durrant xen_device_unbind_event_channel(xendev, channel, NULL); 980c0b336eaSPaul Durrant } 981c0b336eaSPaul Durrant 982094a2239SPaul Durrant xen_device_frontend_destroy(xendev); 983094a2239SPaul Durrant xen_device_backend_destroy(xendev); 984094a2239SPaul Durrant 9854b34b5b1SPaul Durrant if (xendev->xgth) { 986c412ba47SDavid Woodhouse qemu_xen_gnttab_close(xendev->xgth); 9874b34b5b1SPaul Durrant xendev->xgth = NULL; 9884b34b5b1SPaul Durrant } 9894b34b5b1SPaul Durrant 990d198b711SPaul Durrant if (xendev->xsh) { 991ba2a92dbSPaul Durrant qemu_xen_xs_close(xendev->xsh); 992d198b711SPaul Durrant xendev->xsh = NULL; 993d198b711SPaul Durrant } 994d198b711SPaul Durrant 995094a2239SPaul Durrant g_free(xendev->name); 996094a2239SPaul Durrant xendev->name = NULL; 997094a2239SPaul Durrant } 998094a2239SPaul Durrant 999094a2239SPaul Durrant static void xen_device_exit(Notifier *n, void *data) 1000094a2239SPaul Durrant { 1001094a2239SPaul Durrant XenDevice *xendev = container_of(n, XenDevice, exit); 1002094a2239SPaul Durrant 1003b69c3c21SMarkus Armbruster xen_device_unrealize(DEVICE(xendev)); 1004108f7bbaSPaul Durrant } 1005108f7bbaSPaul Durrant 1006108f7bbaSPaul Durrant static void xen_device_realize(DeviceState *dev, Error **errp) 1007108f7bbaSPaul Durrant { 10081de7096dSVladimir Sementsov-Ogievskiy ERRP_GUARD(); 1009108f7bbaSPaul Durrant XenDevice *xendev = XEN_DEVICE(dev); 1010108f7bbaSPaul Durrant XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); 1011094a2239SPaul Durrant XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); 1012108f7bbaSPaul Durrant const char *type = object_get_typename(OBJECT(xendev)); 1013108f7bbaSPaul Durrant 1014094a2239SPaul Durrant if (xendev->frontend_id == DOMID_INVALID) { 1015094a2239SPaul Durrant xendev->frontend_id = xen_domid; 1016094a2239SPaul Durrant } 1017094a2239SPaul Durrant 1018094a2239SPaul Durrant if (xendev->frontend_id >= DOMID_FIRST_RESERVED) { 1019094a2239SPaul Durrant error_setg(errp, "invalid frontend-id"); 1020094a2239SPaul Durrant goto unrealize; 1021094a2239SPaul Durrant } 1022094a2239SPaul Durrant 1023094a2239SPaul Durrant if (!xendev_class->get_name) { 1024094a2239SPaul Durrant error_setg(errp, "get_name method not implemented"); 1025094a2239SPaul Durrant goto unrealize; 1026094a2239SPaul Durrant } 1027094a2239SPaul Durrant 10281de7096dSVladimir Sementsov-Ogievskiy xendev->name = xendev_class->get_name(xendev, errp); 10291de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 10301de7096dSVladimir Sementsov-Ogievskiy error_prepend(errp, "failed to get device name: "); 1031094a2239SPaul Durrant goto unrealize; 1032094a2239SPaul Durrant } 1033094a2239SPaul Durrant 1034094a2239SPaul Durrant trace_xen_device_realize(type, xendev->name); 1035094a2239SPaul Durrant 1036ba2a92dbSPaul Durrant xendev->xsh = qemu_xen_xs_open(); 1037d198b711SPaul Durrant if (!xendev->xsh) { 1038d198b711SPaul Durrant error_setg_errno(errp, errno, "failed xs_open"); 1039d198b711SPaul Durrant goto unrealize; 1040d198b711SPaul Durrant } 1041d198b711SPaul Durrant 1042c412ba47SDavid Woodhouse xendev->xgth = qemu_xen_gnttab_open(); 10434b34b5b1SPaul Durrant if (!xendev->xgth) { 10444b34b5b1SPaul Durrant error_setg_errno(errp, errno, "failed xengnttab_open"); 10454b34b5b1SPaul Durrant goto unrealize; 10464b34b5b1SPaul Durrant } 10474b34b5b1SPaul Durrant 10481de7096dSVladimir Sementsov-Ogievskiy xen_device_backend_create(xendev, errp); 10491de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 1050094a2239SPaul Durrant goto unrealize; 1051094a2239SPaul Durrant } 1052094a2239SPaul Durrant 10531de7096dSVladimir Sementsov-Ogievskiy xen_device_frontend_create(xendev, errp); 10541de7096dSVladimir Sementsov-Ogievskiy if (*errp) { 1055094a2239SPaul Durrant goto unrealize; 1056094a2239SPaul Durrant } 1057108f7bbaSPaul Durrant 1058094a2239SPaul Durrant xen_device_backend_printf(xendev, "frontend", "%s", 1059094a2239SPaul Durrant xendev->frontend_path); 1060094a2239SPaul Durrant xen_device_backend_printf(xendev, "frontend-id", "%u", 1061094a2239SPaul Durrant xendev->frontend_id); 1062094a2239SPaul Durrant xen_device_backend_printf(xendev, "hotplug-status", "connected"); 1063094a2239SPaul Durrant 1064b6af8926SPaul Durrant xen_device_backend_set_online(xendev, true); 1065094a2239SPaul Durrant xen_device_backend_set_state(xendev, XenbusStateInitWait); 1066094a2239SPaul Durrant 10676bd6b955SMark Syms if (!xen_device_frontend_exists(xendev)) { 1068094a2239SPaul Durrant xen_device_frontend_printf(xendev, "backend", "%s", 1069094a2239SPaul Durrant xendev->backend_path); 1070094a2239SPaul Durrant xen_device_frontend_printf(xendev, "backend-id", "%u", 1071094a2239SPaul Durrant xenbus->backend_id); 1072094a2239SPaul Durrant 1073705be570SAnthony PERARD xen_device_frontend_set_state(xendev, XenbusStateInitialising, true); 10746bd6b955SMark Syms } 1075094a2239SPaul Durrant 1076240cc113SPaul Durrant if (xendev_class->realize) { 1077240cc113SPaul Durrant xendev_class->realize(xendev, errp); 1078240cc113SPaul Durrant if (*errp) { 1079240cc113SPaul Durrant goto unrealize; 1080240cc113SPaul Durrant } 1081240cc113SPaul Durrant } 1082240cc113SPaul Durrant 1083094a2239SPaul Durrant xendev->exit.notify = xen_device_exit; 1084094a2239SPaul Durrant qemu_add_exit_notifier(&xendev->exit); 1085108f7bbaSPaul Durrant return; 1086108f7bbaSPaul Durrant 1087108f7bbaSPaul Durrant unrealize: 1088b69c3c21SMarkus Armbruster xen_device_unrealize(dev); 1089108f7bbaSPaul Durrant } 1090108f7bbaSPaul Durrant 1091094a2239SPaul Durrant static Property xen_device_props[] = { 1092094a2239SPaul Durrant DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id, 1093094a2239SPaul Durrant DOMID_INVALID), 1094094a2239SPaul Durrant DEFINE_PROP_END_OF_LIST() 1095094a2239SPaul Durrant }; 1096094a2239SPaul Durrant 1097108f7bbaSPaul Durrant static void xen_device_class_init(ObjectClass *class, void *data) 1098108f7bbaSPaul Durrant { 1099108f7bbaSPaul Durrant DeviceClass *dev_class = DEVICE_CLASS(class); 1100108f7bbaSPaul Durrant 1101108f7bbaSPaul Durrant dev_class->realize = xen_device_realize; 1102108f7bbaSPaul Durrant dev_class->unrealize = xen_device_unrealize; 11034f67d30bSMarc-André Lureau device_class_set_props(dev_class, xen_device_props); 1104108f7bbaSPaul Durrant dev_class->bus_type = TYPE_XEN_BUS; 1105108f7bbaSPaul Durrant } 1106108f7bbaSPaul Durrant 1107108f7bbaSPaul Durrant static const TypeInfo xen_device_type_info = { 1108108f7bbaSPaul Durrant .name = TYPE_XEN_DEVICE, 1109108f7bbaSPaul Durrant .parent = TYPE_DEVICE, 1110108f7bbaSPaul Durrant .instance_size = sizeof(XenDevice), 1111108f7bbaSPaul Durrant .abstract = true, 1112108f7bbaSPaul Durrant .class_size = sizeof(XenDeviceClass), 1113108f7bbaSPaul Durrant .class_init = xen_device_class_init, 1114108f7bbaSPaul Durrant }; 1115108f7bbaSPaul Durrant 1116108f7bbaSPaul Durrant typedef struct XenBridge { 1117108f7bbaSPaul Durrant SysBusDevice busdev; 1118108f7bbaSPaul Durrant } XenBridge; 1119108f7bbaSPaul Durrant 1120108f7bbaSPaul Durrant #define TYPE_XEN_BRIDGE "xen-bridge" 1121108f7bbaSPaul Durrant 1122108f7bbaSPaul Durrant static const TypeInfo xen_bridge_type_info = { 1123108f7bbaSPaul Durrant .name = TYPE_XEN_BRIDGE, 1124108f7bbaSPaul Durrant .parent = TYPE_SYS_BUS_DEVICE, 1125108f7bbaSPaul Durrant .instance_size = sizeof(XenBridge), 1126108f7bbaSPaul Durrant }; 1127108f7bbaSPaul Durrant 1128108f7bbaSPaul Durrant static void xen_register_types(void) 1129108f7bbaSPaul Durrant { 1130108f7bbaSPaul Durrant type_register_static(&xen_bridge_type_info); 1131108f7bbaSPaul Durrant type_register_static(&xen_bus_type_info); 1132108f7bbaSPaul Durrant type_register_static(&xen_device_type_info); 1133108f7bbaSPaul Durrant } 1134108f7bbaSPaul Durrant 1135108f7bbaSPaul Durrant type_init(xen_register_types) 1136108f7bbaSPaul Durrant 1137*7d6eff13SDavid Woodhouse void xen_bus_init(void) 1138108f7bbaSPaul Durrant { 11393e80f690SMarkus Armbruster DeviceState *dev = qdev_new(TYPE_XEN_BRIDGE); 11409388d170SPeter Maydell BusState *bus = qbus_new(TYPE_XEN_BUS, dev, NULL); 1141108f7bbaSPaul Durrant 11423c6ef471SMarkus Armbruster sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 1143cd7c8660SMarkus Armbruster qbus_set_bus_hotplug_handler(bus); 1144c10b4b3cSDavid Woodhouse 1145*7d6eff13SDavid Woodhouse qemu_create_nic_bus_devices(bus, TYPE_XEN_DEVICE, "xen-net-device", 1146*7d6eff13SDavid Woodhouse "xen", "xen-net-device"); 1147108f7bbaSPaul Durrant } 1148