1 /* 2 * Copyright (c) 2018 Citrix Systems Inc. 3 * 4 * This work is licensed under the terms of the GNU GPL, version 2 or later. 5 * See the COPYING file in the top-level directory. 6 */ 7 8 #include "qemu/osdep.h" 9 #include "qemu/error-report.h" 10 #include "qapi/error.h" 11 #include "hw/xen/xen-backend.h" 12 #include "hw/xen/xen-bus.h" 13 14 typedef struct XenBackendImpl { 15 const char *type; 16 XenBackendDeviceCreate create; 17 XenBackendDeviceDestroy destroy; 18 } XenBackendImpl; 19 20 struct XenBackendInstance { 21 QLIST_ENTRY(XenBackendInstance) entry; 22 const XenBackendImpl *impl; 23 XenBus *xenbus; 24 char *name; 25 XenDevice *xendev; 26 }; 27 28 static GHashTable *xen_backend_table_get(void) 29 { 30 static GHashTable *table; 31 32 if (table == NULL) { 33 table = g_hash_table_new(g_str_hash, g_str_equal); 34 } 35 36 return table; 37 } 38 39 static void xen_backend_table_add(XenBackendImpl *impl) 40 { 41 g_hash_table_insert(xen_backend_table_get(), (void *)impl->type, impl); 42 } 43 44 static const XenBackendImpl *xen_backend_table_lookup(const char *type) 45 { 46 return g_hash_table_lookup(xen_backend_table_get(), type); 47 } 48 49 void xen_backend_register(const XenBackendInfo *info) 50 { 51 XenBackendImpl *impl = g_new0(XenBackendImpl, 1); 52 53 g_assert(info->type); 54 55 if (xen_backend_table_lookup(info->type)) { 56 error_report("attempt to register duplicate Xen backend type '%s'", 57 info->type); 58 abort(); 59 } 60 61 if (!info->create) { 62 error_report("backend type '%s' has no creator", info->type); 63 abort(); 64 } 65 66 impl->type = info->type; 67 impl->create = info->create; 68 impl->destroy = info->destroy; 69 70 xen_backend_table_add(impl); 71 } 72 73 static QLIST_HEAD(, XenBackendInstance) backend_list; 74 75 static void xen_backend_list_add(XenBackendInstance *backend) 76 { 77 QLIST_INSERT_HEAD(&backend_list, backend, entry); 78 } 79 80 static XenBackendInstance *xen_backend_list_find(XenDevice *xendev) 81 { 82 XenBackendInstance *backend; 83 84 QLIST_FOREACH(backend, &backend_list, entry) { 85 if (backend->xendev == xendev) { 86 return backend; 87 } 88 } 89 90 return NULL; 91 } 92 93 static void xen_backend_list_remove(XenBackendInstance *backend) 94 { 95 QLIST_REMOVE(backend, entry); 96 } 97 98 void xen_backend_device_create(XenBus *xenbus, const char *type, 99 const char *name, QDict *opts, Error **errp) 100 { 101 const XenBackendImpl *impl = xen_backend_table_lookup(type); 102 XenBackendInstance *backend; 103 Error *local_error = NULL; 104 105 if (!impl) { 106 return; 107 } 108 109 backend = g_new0(XenBackendInstance, 1); 110 backend->xenbus = xenbus; 111 backend->name = g_strdup(name); 112 113 impl->create(backend, opts, &local_error); 114 if (local_error) { 115 error_propagate(errp, local_error); 116 g_free(backend->name); 117 g_free(backend); 118 return; 119 } 120 121 backend->impl = impl; 122 xen_backend_list_add(backend); 123 } 124 125 XenBus *xen_backend_get_bus(XenBackendInstance *backend) 126 { 127 return backend->xenbus; 128 } 129 130 const char *xen_backend_get_name(XenBackendInstance *backend) 131 { 132 return backend->name; 133 } 134 135 void xen_backend_set_device(XenBackendInstance *backend, 136 XenDevice *xendev) 137 { 138 g_assert(!backend->xendev); 139 backend->xendev = xendev; 140 } 141 142 XenDevice *xen_backend_get_device(XenBackendInstance *backend) 143 { 144 return backend->xendev; 145 } 146 147 148 bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp) 149 { 150 XenBackendInstance *backend = xen_backend_list_find(xendev); 151 const XenBackendImpl *impl; 152 153 if (!backend) { 154 return false; 155 } 156 157 impl = backend->impl; 158 impl->destroy(backend, errp); 159 160 xen_backend_list_remove(backend); 161 g_free(backend->name); 162 g_free(backend); 163 164 return true; 165 } 166