| /* |
| * 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; |
| } |