diff options
Diffstat (limited to 'hw/xen/xen-backend.c')
-rw-r--r-- | hw/xen/xen-backend.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/hw/xen/xen-backend.c b/hw/xen/xen-backend.c new file mode 100644 index 0000000000..da065f81b7 --- /dev/null +++ b/hw/xen/xen-backend.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018 Citrix Systems Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/xen/xen-backend.h" +#include "hw/xen/xen-bus.h" + +typedef struct XenBackendImpl { + const char *type; + XenBackendDeviceCreate create; + XenBackendDeviceDestroy destroy; +} XenBackendImpl; + +struct XenBackendInstance { + QLIST_ENTRY(XenBackendInstance) entry; + const XenBackendImpl *impl; + XenBus *xenbus; + char *name; + XenDevice *xendev; +}; + +static GHashTable *xen_backend_table_get(void) +{ + static GHashTable *table; + + if (table == NULL) { + table = g_hash_table_new(g_str_hash, g_str_equal); + } + + return table; +} + +static void xen_backend_table_add(XenBackendImpl *impl) +{ + g_hash_table_insert(xen_backend_table_get(), (void *)impl->type, impl); +} + +static const XenBackendImpl *xen_backend_table_lookup(const char *type) +{ + return g_hash_table_lookup(xen_backend_table_get(), type); +} + +void xen_backend_register(const XenBackendInfo *info) +{ + XenBackendImpl *impl = g_new0(XenBackendImpl, 1); + + g_assert(info->type); + + if (xen_backend_table_lookup(info->type)) { + error_report("attempt to register duplicate Xen backend type '%s'", + info->type); + abort(); + } + + if (!info->create) { + error_report("backend type '%s' has no creator", info->type); + abort(); + } + + impl->type = info->type; + impl->create = info->create; + impl->destroy = info->destroy; + + xen_backend_table_add(impl); +} + +static QLIST_HEAD(, XenBackendInstance) backend_list; + +static void xen_backend_list_add(XenBackendInstance *backend) +{ + QLIST_INSERT_HEAD(&backend_list, backend, entry); +} + +static XenBackendInstance *xen_backend_list_find(XenDevice *xendev) +{ + XenBackendInstance *backend; + + QLIST_FOREACH(backend, &backend_list, entry) { + if (backend->xendev == xendev) { + return backend; + } + } + + return NULL; +} + +static void xen_backend_list_remove(XenBackendInstance *backend) +{ + QLIST_REMOVE(backend, entry); +} + +void xen_backend_device_create(XenBus *xenbus, const char *type, + const char *name, QDict *opts, Error **errp) +{ + const XenBackendImpl *impl = xen_backend_table_lookup(type); + XenBackendInstance *backend; + Error *local_error = NULL; + + if (!impl) { + return; + } + + backend = g_new0(XenBackendInstance, 1); + backend->xenbus = xenbus; + backend->name = g_strdup(name); + + impl->create(backend, opts, &local_error); + if (local_error) { + error_propagate(errp, local_error); + g_free(backend->name); + g_free(backend); + return; + } + + backend->impl = impl; + xen_backend_list_add(backend); +} + +XenBus *xen_backend_get_bus(XenBackendInstance *backend) +{ + return backend->xenbus; +} + +const char *xen_backend_get_name(XenBackendInstance *backend) +{ + return backend->name; +} + +void xen_backend_set_device(XenBackendInstance *backend, + XenDevice *xendev) +{ + g_assert(!backend->xendev); + backend->xendev = xendev; +} + +XenDevice *xen_backend_get_device(XenBackendInstance *backend) +{ + return backend->xendev; +} + + +bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp) +{ + XenBackendInstance *backend = xen_backend_list_find(xendev); + const XenBackendImpl *impl; + + if (!backend) { + return false; + } + + impl = backend->impl; + impl->destroy(backend, errp); + + xen_backend_list_remove(backend); + g_free(backend->name); + g_free(backend); + + return true; +} |