aboutsummaryrefslogtreecommitdiff
path: root/util/yank.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/yank.c')
-rw-r--r--util/yank.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/util/yank.c b/util/yank.c
new file mode 100644
index 0000000000..fc08f65209
--- /dev/null
+++ b/util/yank.c
@@ -0,0 +1,207 @@
+/*
+ * QEMU yank feature
+ *
+ * Copyright (c) Lukas Straub <lukasstraub2@web.de>
+ *
+ * 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 "qapi/error.h"
+#include "qemu/thread.h"
+#include "qemu/queue.h"
+#include "qemu/lockable.h"
+#include "qapi/qapi-commands-yank.h"
+#include "qapi/qapi-visit-yank.h"
+#include "qapi/clone-visitor.h"
+#include "io/channel.h"
+#include "qemu/yank.h"
+
+struct YankFuncAndParam {
+ YankFn *func;
+ void *opaque;
+ QLIST_ENTRY(YankFuncAndParam) next;
+};
+
+struct YankInstanceEntry {
+ YankInstance *instance;
+ QLIST_HEAD(, YankFuncAndParam) yankfns;
+ QLIST_ENTRY(YankInstanceEntry) next;
+};
+
+typedef struct YankFuncAndParam YankFuncAndParam;
+typedef struct YankInstanceEntry YankInstanceEntry;
+
+/*
+ * This lock protects the yank_instance_list below. Because it's taken by
+ * OOB-capable commands, it must be "fast", i.e. it may only be held for a
+ * bounded, short time. See docs/devel/qapi-code-gen.txt for additional
+ * information.
+ */
+static QemuMutex yank_lock;
+
+static QLIST_HEAD(, YankInstanceEntry) yank_instance_list
+ = QLIST_HEAD_INITIALIZER(yank_instance_list);
+
+static bool yank_instance_equal(const YankInstance *a, const YankInstance *b)
+{
+ if (a->type != b->type) {
+ return false;
+ }
+
+ switch (a->type) {
+ case YANK_INSTANCE_TYPE_BLOCK_NODE:
+ return g_str_equal(a->u.block_node.node_name,
+ b->u.block_node.node_name);
+
+ case YANK_INSTANCE_TYPE_CHARDEV:
+ return g_str_equal(a->u.chardev.id, b->u.chardev.id);
+
+ case YANK_INSTANCE_TYPE_MIGRATION:
+ return true;
+
+ default:
+ abort();
+ }
+}
+
+static YankInstanceEntry *yank_find_entry(const YankInstance *instance)
+{
+ YankInstanceEntry *entry;
+
+ QLIST_FOREACH(entry, &yank_instance_list, next) {
+ if (yank_instance_equal(entry->instance, instance)) {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+bool yank_register_instance(const YankInstance *instance, Error **errp)
+{
+ YankInstanceEntry *entry;
+
+ QEMU_LOCK_GUARD(&yank_lock);
+
+ if (yank_find_entry(instance)) {
+ error_setg(errp, "duplicate yank instance");
+ return false;
+ }
+
+ entry = g_new0(YankInstanceEntry, 1);
+ entry->instance = QAPI_CLONE(YankInstance, instance);
+ QLIST_INIT(&entry->yankfns);
+ QLIST_INSERT_HEAD(&yank_instance_list, entry, next);
+
+ return true;
+}
+
+void yank_unregister_instance(const YankInstance *instance)
+{
+ YankInstanceEntry *entry;
+
+ QEMU_LOCK_GUARD(&yank_lock);
+ entry = yank_find_entry(instance);
+ assert(entry);
+
+ assert(QLIST_EMPTY(&entry->yankfns));
+ QLIST_REMOVE(entry, next);
+ qapi_free_YankInstance(entry->instance);
+ g_free(entry);
+}
+
+void yank_register_function(const YankInstance *instance,
+ YankFn *func,
+ void *opaque)
+{
+ YankInstanceEntry *entry;
+ YankFuncAndParam *func_entry;
+
+ QEMU_LOCK_GUARD(&yank_lock);
+ entry = yank_find_entry(instance);
+ assert(entry);
+
+ func_entry = g_new0(YankFuncAndParam, 1);
+ func_entry->func = func;
+ func_entry->opaque = opaque;
+
+ QLIST_INSERT_HEAD(&entry->yankfns, func_entry, next);
+}
+
+void yank_unregister_function(const YankInstance *instance,
+ YankFn *func,
+ void *opaque)
+{
+ YankInstanceEntry *entry;
+ YankFuncAndParam *func_entry;
+
+ QEMU_LOCK_GUARD(&yank_lock);
+ entry = yank_find_entry(instance);
+ assert(entry);
+
+ QLIST_FOREACH(func_entry, &entry->yankfns, next) {
+ if (func_entry->func == func && func_entry->opaque == opaque) {
+ QLIST_REMOVE(func_entry, next);
+ g_free(func_entry);
+ return;
+ }
+ }
+
+ abort();
+}
+
+void yank_generic_iochannel(void *opaque)
+{
+ QIOChannel *ioc = QIO_CHANNEL(opaque);
+
+ qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+}
+
+void qmp_yank(YankInstanceList *instances,
+ Error **errp)
+{
+ YankInstanceList *tail;
+ YankInstanceEntry *entry;
+ YankFuncAndParam *func_entry;
+
+ QEMU_LOCK_GUARD(&yank_lock);
+ for (tail = instances; tail; tail = tail->next) {
+ entry = yank_find_entry(tail->value);
+ if (!entry) {
+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Instance not found");
+ return;
+ }
+ }
+ for (tail = instances; tail; tail = tail->next) {
+ entry = yank_find_entry(tail->value);
+ assert(entry);
+ QLIST_FOREACH(func_entry, &entry->yankfns, next) {
+ func_entry->func(func_entry->opaque);
+ }
+ }
+}
+
+YankInstanceList *qmp_query_yank(Error **errp)
+{
+ YankInstanceEntry *entry;
+ YankInstanceList *ret;
+
+ ret = NULL;
+
+ QEMU_LOCK_GUARD(&yank_lock);
+ QLIST_FOREACH(entry, &yank_instance_list, next) {
+ YankInstanceList *new_entry;
+ new_entry = g_new0(YankInstanceList, 1);
+ new_entry->value = QAPI_CLONE(YankInstance, entry->instance);
+ new_entry->next = ret;
+ ret = new_entry;
+ }
+
+ return ret;
+}
+
+static void __attribute__((__constructor__)) yank_init(void)
+{
+ qemu_mutex_init(&yank_lock);
+}