]> git.proxmox.com Git - mirror_qemu.git/blame - util/thread-context.c
util/defer-call: move defer_call() to util/
[mirror_qemu.git] / util / thread-context.c
CommitLineData
e2de2c49
DH
1/*
2 * QEMU Thread Context
3 *
4 * Copyright Red Hat Inc., 2022
5 *
6 * Authors:
7 * David Hildenbrand <david@redhat.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 */
12
13#include "qemu/osdep.h"
14#include "qemu/thread-context.h"
15#include "qapi/error.h"
16#include "qapi/qapi-builtin-visit.h"
17#include "qapi/visitor.h"
18#include "qemu/config-file.h"
19#include "qapi/qapi-builtin-visit.h"
20#include "qom/object_interfaces.h"
21#include "qemu/module.h"
22#include "qemu/bitmap.h"
23
10218ae6
DH
24#ifdef CONFIG_NUMA
25#include <numa.h>
26#endif
27
e2de2c49
DH
28enum {
29 TC_CMD_NONE = 0,
30 TC_CMD_STOP,
31 TC_CMD_NEW,
32};
33
34typedef struct ThreadContextCmdNew {
35 QemuThread *thread;
36 const char *name;
37 void *(*start_routine)(void *);
38 void *arg;
39 int mode;
40} ThreadContextCmdNew;
41
42static void *thread_context_run(void *opaque)
43{
44 ThreadContext *tc = opaque;
45
46 tc->thread_id = qemu_get_thread_id();
47 qemu_sem_post(&tc->sem);
48
49 while (true) {
50 /*
51 * Threads inherit the CPU affinity of the creating thread. For this
52 * reason, we create new (especially short-lived) threads from our
53 * persistent context thread.
54 *
55 * Especially when QEMU is not allowed to set the affinity itself,
56 * management tools can simply set the affinity of the context thread
57 * after creating the context, to have new threads created via
58 * the context inherit the CPU affinity automatically.
59 */
60 switch (tc->thread_cmd) {
61 case TC_CMD_NONE:
62 break;
63 case TC_CMD_STOP:
64 tc->thread_cmd = TC_CMD_NONE;
65 qemu_sem_post(&tc->sem);
66 return NULL;
67 case TC_CMD_NEW: {
68 ThreadContextCmdNew *cmd_new = tc->thread_cmd_data;
69
70 qemu_thread_create(cmd_new->thread, cmd_new->name,
71 cmd_new->start_routine, cmd_new->arg,
72 cmd_new->mode);
73 tc->thread_cmd = TC_CMD_NONE;
74 tc->thread_cmd_data = NULL;
75 qemu_sem_post(&tc->sem);
76 break;
77 }
78 default:
79 g_assert_not_reached();
80 }
81 qemu_sem_wait(&tc->sem_thread);
82 }
83}
84
85static void thread_context_set_cpu_affinity(Object *obj, Visitor *v,
86 const char *name, void *opaque,
87 Error **errp)
88{
89 ThreadContext *tc = THREAD_CONTEXT(obj);
90 uint16List *l, *host_cpus = NULL;
91 unsigned long *bitmap = NULL;
92 int nbits = 0, ret;
e2de2c49 93
10218ae6
DH
94 if (tc->init_cpu_bitmap) {
95 error_setg(errp, "Mixing CPU and node affinity not supported");
96 return;
97 }
98
d1c81c34 99 if (!visit_type_uint16List(v, name, &host_cpus, errp)) {
e2de2c49
DH
100 return;
101 }
102
103 if (!host_cpus) {
104 error_setg(errp, "CPU list is empty");
105 goto out;
106 }
107
108 for (l = host_cpus; l; l = l->next) {
109 nbits = MAX(nbits, l->value + 1);
110 }
111 bitmap = bitmap_new(nbits);
112 for (l = host_cpus; l; l = l->next) {
113 set_bit(l->value, bitmap);
114 }
115
116 if (tc->thread_id != -1) {
117 /*
118 * Note: we won't be adjusting the affinity of any thread that is still
119 * around, but only the affinity of the context thread.
120 */
121 ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits);
122 if (ret) {
123 error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
124 }
125 } else {
126 tc->init_cpu_bitmap = bitmap;
127 bitmap = NULL;
128 tc->init_cpu_nbits = nbits;
129 }
130out:
131 g_free(bitmap);
132 qapi_free_uint16List(host_cpus);
133}
134
135static void thread_context_get_cpu_affinity(Object *obj, Visitor *v,
136 const char *name, void *opaque,
137 Error **errp)
138{
139 unsigned long *bitmap, nbits, value;
140 ThreadContext *tc = THREAD_CONTEXT(obj);
141 uint16List *host_cpus = NULL;
142 uint16List **tail = &host_cpus;
143 int ret;
144
145 if (tc->thread_id == -1) {
146 error_setg(errp, "Object not initialized yet");
147 return;
148 }
149
150 ret = qemu_thread_get_affinity(&tc->thread, &bitmap, &nbits);
151 if (ret) {
152 error_setg(errp, "Getting CPU affinity failed: %s", strerror(ret));
153 return;
154 }
155
156 value = find_first_bit(bitmap, nbits);
157 while (value < nbits) {
158 QAPI_LIST_APPEND(tail, value);
159
160 value = find_next_bit(bitmap, nbits, value + 1);
161 }
162 g_free(bitmap);
163
164 visit_type_uint16List(v, name, &host_cpus, errp);
165 qapi_free_uint16List(host_cpus);
166}
167
10218ae6
DH
168static void thread_context_set_node_affinity(Object *obj, Visitor *v,
169 const char *name, void *opaque,
170 Error **errp)
171{
172#ifdef CONFIG_NUMA
173 const int nbits = numa_num_possible_cpus();
174 ThreadContext *tc = THREAD_CONTEXT(obj);
175 uint16List *l, *host_nodes = NULL;
176 unsigned long *bitmap = NULL;
177 struct bitmask *tmp_cpus;
10218ae6
DH
178 int ret, i;
179
180 if (tc->init_cpu_bitmap) {
181 error_setg(errp, "Mixing CPU and node affinity not supported");
182 return;
183 }
184
d1c81c34 185 if (!visit_type_uint16List(v, name, &host_nodes, errp)) {
10218ae6
DH
186 return;
187 }
188
189 if (!host_nodes) {
190 error_setg(errp, "Node list is empty");
191 goto out;
192 }
193
194 bitmap = bitmap_new(nbits);
195 tmp_cpus = numa_allocate_cpumask();
196 for (l = host_nodes; l; l = l->next) {
197 numa_bitmask_clearall(tmp_cpus);
198 ret = numa_node_to_cpus(l->value, tmp_cpus);
199 if (ret) {
200 /* We ignore any errors, such as impossible nodes. */
201 continue;
202 }
203 for (i = 0; i < nbits; i++) {
204 if (numa_bitmask_isbitset(tmp_cpus, i)) {
205 set_bit(i, bitmap);
206 }
207 }
208 }
209 numa_free_cpumask(tmp_cpus);
210
211 if (bitmap_empty(bitmap, nbits)) {
212 error_setg(errp, "The nodes select no CPUs");
213 goto out;
214 }
215
216 if (tc->thread_id != -1) {
217 /*
218 * Note: we won't be adjusting the affinity of any thread that is still
219 * around for now, but only the affinity of the context thread.
220 */
221 ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits);
222 if (ret) {
223 error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
224 }
225 } else {
226 tc->init_cpu_bitmap = bitmap;
227 bitmap = NULL;
228 tc->init_cpu_nbits = nbits;
229 }
230out:
231 g_free(bitmap);
232 qapi_free_uint16List(host_nodes);
233#else
234 error_setg(errp, "NUMA node affinity is not supported by this QEMU");
235#endif
236}
237
e2de2c49
DH
238static void thread_context_get_thread_id(Object *obj, Visitor *v,
239 const char *name, void *opaque,
240 Error **errp)
241{
242 ThreadContext *tc = THREAD_CONTEXT(obj);
243 uint64_t value = tc->thread_id;
244
245 visit_type_uint64(v, name, &value, errp);
246}
247
248static void thread_context_instance_complete(UserCreatable *uc, Error **errp)
249{
250 ThreadContext *tc = THREAD_CONTEXT(uc);
251 char *thread_name;
252 int ret;
253
254 thread_name = g_strdup_printf("TC %s",
255 object_get_canonical_path_component(OBJECT(uc)));
256 qemu_thread_create(&tc->thread, thread_name, thread_context_run, tc,
257 QEMU_THREAD_JOINABLE);
258 g_free(thread_name);
259
260 /* Wait until initialization of the thread is done. */
261 while (tc->thread_id == -1) {
262 qemu_sem_wait(&tc->sem);
263 }
264
265 if (tc->init_cpu_bitmap) {
266 ret = qemu_thread_set_affinity(&tc->thread, tc->init_cpu_bitmap,
267 tc->init_cpu_nbits);
268 if (ret) {
269 error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
270 }
271 g_free(tc->init_cpu_bitmap);
272 tc->init_cpu_bitmap = NULL;
273 }
274}
275
276static void thread_context_class_init(ObjectClass *oc, void *data)
277{
278 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
279
280 ucc->complete = thread_context_instance_complete;
281 object_class_property_add(oc, "thread-id", "int",
282 thread_context_get_thread_id, NULL, NULL,
283 NULL);
284 object_class_property_add(oc, "cpu-affinity", "int",
285 thread_context_get_cpu_affinity,
286 thread_context_set_cpu_affinity, NULL, NULL);
10218ae6
DH
287 object_class_property_add(oc, "node-affinity", "int", NULL,
288 thread_context_set_node_affinity, NULL, NULL);
e2de2c49
DH
289}
290
291static void thread_context_instance_init(Object *obj)
292{
293 ThreadContext *tc = THREAD_CONTEXT(obj);
294
295 tc->thread_id = -1;
296 qemu_sem_init(&tc->sem, 0);
297 qemu_sem_init(&tc->sem_thread, 0);
298 qemu_mutex_init(&tc->mutex);
299}
300
301static void thread_context_instance_finalize(Object *obj)
302{
303 ThreadContext *tc = THREAD_CONTEXT(obj);
304
305 if (tc->thread_id != -1) {
306 tc->thread_cmd = TC_CMD_STOP;
307 qemu_sem_post(&tc->sem_thread);
308 qemu_thread_join(&tc->thread);
309 }
310 qemu_sem_destroy(&tc->sem);
311 qemu_sem_destroy(&tc->sem_thread);
312 qemu_mutex_destroy(&tc->mutex);
313}
314
315static const TypeInfo thread_context_info = {
316 .name = TYPE_THREAD_CONTEXT,
317 .parent = TYPE_OBJECT,
318 .class_init = thread_context_class_init,
319 .instance_size = sizeof(ThreadContext),
320 .instance_init = thread_context_instance_init,
321 .instance_finalize = thread_context_instance_finalize,
322 .interfaces = (InterfaceInfo[]) {
323 { TYPE_USER_CREATABLE },
324 { }
325 }
326};
327
328static void thread_context_register_types(void)
329{
330 type_register_static(&thread_context_info);
331}
332type_init(thread_context_register_types)
333
334void thread_context_create_thread(ThreadContext *tc, QemuThread *thread,
335 const char *name,
336 void *(*start_routine)(void *), void *arg,
337 int mode)
338{
339 ThreadContextCmdNew data = {
340 .thread = thread,
341 .name = name,
342 .start_routine = start_routine,
343 .arg = arg,
344 .mode = mode,
345 };
346
347 qemu_mutex_lock(&tc->mutex);
348 tc->thread_cmd = TC_CMD_NEW;
349 tc->thread_cmd_data = &data;
350 qemu_sem_post(&tc->sem_thread);
351
352 while (tc->thread_cmd != TC_CMD_NONE) {
353 qemu_sem_wait(&tc->sem);
354 }
355 qemu_mutex_unlock(&tc->mutex);
356}