xref: /qemu/util/yank.c (revision 73b49878)
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