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 20 struct YankFuncAndParam { 21 YankFn *func; 22 void *opaque; 23 QLIST_ENTRY(YankFuncAndParam) next; 24 }; 25 26 struct YankInstanceEntry { 27 YankInstance *instance; 28 QLIST_HEAD(, YankFuncAndParam) yankfns; 29 QLIST_ENTRY(YankInstanceEntry) next; 30 }; 31 32 typedef struct YankFuncAndParam YankFuncAndParam; 33 typedef 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.rst for additional 39 * information. 40 */ 41 static QemuMutex yank_lock; 42 43 static QLIST_HEAD(, YankInstanceEntry) yank_instance_list 44 = QLIST_HEAD_INITIALIZER(yank_instance_list); 45 46 static 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 68 static 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 80 bool 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 99 void 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 113 void 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 131 void 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 153 void 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 177 YankInstanceList *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 196 static void __attribute__((__constructor__)) yank_init(void) 197 { 198 qemu_mutex_init(&yank_lock); 199 } 200