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