qemu/util/yank.c
<<
>>
Prefs
   1/*
   2 * QEMU yank feature
   3 *
   4 * Copyright (c) Lukas Straub <lukasstraub2@web.de>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   7 * See the COPYING file in the top-level directory.
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "qapi/error.h"
  12#include "qemu/thread.h"
  13#include "qemu/queue.h"
  14#include "qemu/lockable.h"
  15#include "qapi/qapi-commands-yank.h"
  16#include "qapi/qapi-visit-yank.h"
  17#include "qapi/clone-visitor.h"
  18#include "qemu/yank.h"
  19
  20struct YankFuncAndParam {
  21    YankFn *func;
  22    void *opaque;
  23    QLIST_ENTRY(YankFuncAndParam) next;
  24};
  25
  26struct YankInstanceEntry {
  27    YankInstance *instance;
  28    QLIST_HEAD(, YankFuncAndParam) yankfns;
  29    QLIST_ENTRY(YankInstanceEntry) next;
  30};
  31
  32typedef struct YankFuncAndParam YankFuncAndParam;
  33typedef struct YankInstanceEntry YankInstanceEntry;
  34
  35/*
  36 * This lock protects the yank_instance_list below. Because it's taken by
  37 * OOB-capable commands, it must be "fast", i.e. it may only be held for a
  38 * bounded, short time. See docs/devel/qapi-code-gen.txt for additional
  39 * information.
  40 */
  41static QemuMutex yank_lock;
  42
  43static QLIST_HEAD(, YankInstanceEntry) yank_instance_list
  44    = QLIST_HEAD_INITIALIZER(yank_instance_list);
  45
  46static bool yank_instance_equal(const YankInstance *a, const YankInstance *b)
  47{
  48    if (a->type != b->type) {
  49        return false;
  50    }
  51
  52    switch (a->type) {
  53    case YANK_INSTANCE_TYPE_BLOCK_NODE:
  54        return g_str_equal(a->u.block_node.node_name,
  55                           b->u.block_node.node_name);
  56
  57    case YANK_INSTANCE_TYPE_CHARDEV:
  58        return g_str_equal(a->u.chardev.id, b->u.chardev.id);
  59
  60    case YANK_INSTANCE_TYPE_MIGRATION:
  61        return true;
  62
  63    default:
  64        abort();
  65    }
  66}
  67
  68static YankInstanceEntry *yank_find_entry(const YankInstance *instance)
  69{
  70    YankInstanceEntry *entry;
  71
  72    QLIST_FOREACH(entry, &yank_instance_list, next) {
  73        if (yank_instance_equal(entry->instance, instance)) {
  74            return entry;
  75        }
  76    }
  77    return NULL;
  78}
  79
  80bool yank_register_instance(const YankInstance *instance, Error **errp)
  81{
  82    YankInstanceEntry *entry;
  83
  84    QEMU_LOCK_GUARD(&yank_lock);
  85
  86    if (yank_find_entry(instance)) {
  87        error_setg(errp, "duplicate yank instance");
  88        return false;
  89    }
  90
  91    entry = g_new0(YankInstanceEntry, 1);
  92    entry->instance = QAPI_CLONE(YankInstance, instance);
  93    QLIST_INIT(&entry->yankfns);
  94    QLIST_INSERT_HEAD(&yank_instance_list, entry, next);
  95
  96    return true;
  97}
  98
  99void yank_unregister_instance(const YankInstance *instance)
 100{
 101    YankInstanceEntry *entry;
 102
 103    QEMU_LOCK_GUARD(&yank_lock);
 104    entry = yank_find_entry(instance);
 105    assert(entry);
 106
 107    assert(QLIST_EMPTY(&entry->yankfns));
 108    QLIST_REMOVE(entry, next);
 109    qapi_free_YankInstance(entry->instance);
 110    g_free(entry);
 111}
 112
 113void yank_register_function(const YankInstance *instance,
 114                            YankFn *func,
 115                            void *opaque)
 116{
 117    YankInstanceEntry *entry;
 118    YankFuncAndParam *func_entry;
 119
 120    QEMU_LOCK_GUARD(&yank_lock);
 121    entry = yank_find_entry(instance);
 122    assert(entry);
 123
 124    func_entry = g_new0(YankFuncAndParam, 1);
 125    func_entry->func = func;
 126    func_entry->opaque = opaque;
 127
 128    QLIST_INSERT_HEAD(&entry->yankfns, func_entry, next);
 129}
 130
 131void yank_unregister_function(const YankInstance *instance,
 132                              YankFn *func,
 133                              void *opaque)
 134{
 135    YankInstanceEntry *entry;
 136    YankFuncAndParam *func_entry;
 137
 138    QEMU_LOCK_GUARD(&yank_lock);
 139    entry = yank_find_entry(instance);
 140    assert(entry);
 141
 142    QLIST_FOREACH(func_entry, &entry->yankfns, next) {
 143        if (func_entry->func == func && func_entry->opaque == opaque) {
 144            QLIST_REMOVE(func_entry, next);
 145            g_free(func_entry);
 146            return;
 147        }
 148    }
 149
 150    abort();
 151}
 152
 153void qmp_yank(YankInstanceList *instances,
 154              Error **errp)
 155{
 156    YankInstanceList *tail;
 157    YankInstanceEntry *entry;
 158    YankFuncAndParam *func_entry;
 159
 160    QEMU_LOCK_GUARD(&yank_lock);
 161    for (tail = instances; tail; tail = tail->next) {
 162        entry = yank_find_entry(tail->value);
 163        if (!entry) {
 164            error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Instance not found");
 165            return;
 166        }
 167    }
 168    for (tail = instances; tail; tail = tail->next) {
 169        entry = yank_find_entry(tail->value);
 170        assert(entry);
 171        QLIST_FOREACH(func_entry, &entry->yankfns, next) {
 172            func_entry->func(func_entry->opaque);
 173        }
 174    }
 175}
 176
 177YankInstanceList *qmp_query_yank(Error **errp)
 178{
 179    YankInstanceEntry *entry;
 180    YankInstanceList *ret;
 181
 182    ret = NULL;
 183
 184    QEMU_LOCK_GUARD(&yank_lock);
 185    QLIST_FOREACH(entry, &yank_instance_list, next) {
 186        YankInstanceList *new_entry;
 187        new_entry = g_new0(YankInstanceList, 1);
 188        new_entry->value = QAPI_CLONE(YankInstance, entry->instance);
 189        new_entry->next = ret;
 190        ret = new_entry;
 191    }
 192
 193    return ret;
 194}
 195
 196static void __attribute__((__constructor__)) yank_init(void)
 197{
 198    qemu_mutex_init(&yank_lock);
 199}
 200