1
2
3
4
5
6
7
8
9
10
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
24#ifdef CONFIG_NUMA
25#include <numa.h>
26#endif
27
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
52
53
54
55
56
57
58
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;
93 Error *err = NULL;
94
95 if (tc->init_cpu_bitmap) {
96 error_setg(errp, "Mixing CPU and node affinity not supported");
97 return;
98 }
99
100 visit_type_uint16List(v, name, &host_cpus, &err);
101 if (err) {
102 error_propagate(errp, err);
103 return;
104 }
105
106 if (!host_cpus) {
107 error_setg(errp, "CPU list is empty");
108 goto out;
109 }
110
111 for (l = host_cpus; l; l = l->next) {
112 nbits = MAX(nbits, l->value + 1);
113 }
114 bitmap = bitmap_new(nbits);
115 for (l = host_cpus; l; l = l->next) {
116 set_bit(l->value, bitmap);
117 }
118
119 if (tc->thread_id != -1) {
120
121
122
123
124 ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits);
125 if (ret) {
126 error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
127 }
128 } else {
129 tc->init_cpu_bitmap = bitmap;
130 bitmap = NULL;
131 tc->init_cpu_nbits = nbits;
132 }
133out:
134 g_free(bitmap);
135 qapi_free_uint16List(host_cpus);
136}
137
138static void thread_context_get_cpu_affinity(Object *obj, Visitor *v,
139 const char *name, void *opaque,
140 Error **errp)
141{
142 unsigned long *bitmap, nbits, value;
143 ThreadContext *tc = THREAD_CONTEXT(obj);
144 uint16List *host_cpus = NULL;
145 uint16List **tail = &host_cpus;
146 int ret;
147
148 if (tc->thread_id == -1) {
149 error_setg(errp, "Object not initialized yet");
150 return;
151 }
152
153 ret = qemu_thread_get_affinity(&tc->thread, &bitmap, &nbits);
154 if (ret) {
155 error_setg(errp, "Getting CPU affinity failed: %s", strerror(ret));
156 return;
157 }
158
159 value = find_first_bit(bitmap, nbits);
160 while (value < nbits) {
161 QAPI_LIST_APPEND(tail, value);
162
163 value = find_next_bit(bitmap, nbits, value + 1);
164 }
165 g_free(bitmap);
166
167 visit_type_uint16List(v, name, &host_cpus, errp);
168 qapi_free_uint16List(host_cpus);
169}
170
171static void thread_context_set_node_affinity(Object *obj, Visitor *v,
172 const char *name, void *opaque,
173 Error **errp)
174{
175#ifdef CONFIG_NUMA
176 const int nbits = numa_num_possible_cpus();
177 ThreadContext *tc = THREAD_CONTEXT(obj);
178 uint16List *l, *host_nodes = NULL;
179 unsigned long *bitmap = NULL;
180 struct bitmask *tmp_cpus;
181 Error *err = NULL;
182 int ret, i;
183
184 if (tc->init_cpu_bitmap) {
185 error_setg(errp, "Mixing CPU and node affinity not supported");
186 return;
187 }
188
189 visit_type_uint16List(v, name, &host_nodes, &err);
190 if (err) {
191 error_propagate(errp, err);
192 return;
193 }
194
195 if (!host_nodes) {
196 error_setg(errp, "Node list is empty");
197 goto out;
198 }
199
200 bitmap = bitmap_new(nbits);
201 tmp_cpus = numa_allocate_cpumask();
202 for (l = host_nodes; l; l = l->next) {
203 numa_bitmask_clearall(tmp_cpus);
204 ret = numa_node_to_cpus(l->value, tmp_cpus);
205 if (ret) {
206
207 continue;
208 }
209 for (i = 0; i < nbits; i++) {
210 if (numa_bitmask_isbitset(tmp_cpus, i)) {
211 set_bit(i, bitmap);
212 }
213 }
214 }
215 numa_free_cpumask(tmp_cpus);
216
217 if (bitmap_empty(bitmap, nbits)) {
218 error_setg(errp, "The nodes select no CPUs");
219 goto out;
220 }
221
222 if (tc->thread_id != -1) {
223
224
225
226
227 ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits);
228 if (ret) {
229 error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
230 }
231 } else {
232 tc->init_cpu_bitmap = bitmap;
233 bitmap = NULL;
234 tc->init_cpu_nbits = nbits;
235 }
236out:
237 g_free(bitmap);
238 qapi_free_uint16List(host_nodes);
239#else
240 error_setg(errp, "NUMA node affinity is not supported by this QEMU");
241#endif
242}
243
244static void thread_context_get_thread_id(Object *obj, Visitor *v,
245 const char *name, void *opaque,
246 Error **errp)
247{
248 ThreadContext *tc = THREAD_CONTEXT(obj);
249 uint64_t value = tc->thread_id;
250
251 visit_type_uint64(v, name, &value, errp);
252}
253
254static void thread_context_instance_complete(UserCreatable *uc, Error **errp)
255{
256 ThreadContext *tc = THREAD_CONTEXT(uc);
257 char *thread_name;
258 int ret;
259
260 thread_name = g_strdup_printf("TC %s",
261 object_get_canonical_path_component(OBJECT(uc)));
262 qemu_thread_create(&tc->thread, thread_name, thread_context_run, tc,
263 QEMU_THREAD_JOINABLE);
264 g_free(thread_name);
265
266
267 while (tc->thread_id == -1) {
268 qemu_sem_wait(&tc->sem);
269 }
270
271 if (tc->init_cpu_bitmap) {
272 ret = qemu_thread_set_affinity(&tc->thread, tc->init_cpu_bitmap,
273 tc->init_cpu_nbits);
274 if (ret) {
275 error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
276 }
277 g_free(tc->init_cpu_bitmap);
278 tc->init_cpu_bitmap = NULL;
279 }
280}
281
282static void thread_context_class_init(ObjectClass *oc, void *data)
283{
284 UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
285
286 ucc->complete = thread_context_instance_complete;
287 object_class_property_add(oc, "thread-id", "int",
288 thread_context_get_thread_id, NULL, NULL,
289 NULL);
290 object_class_property_add(oc, "cpu-affinity", "int",
291 thread_context_get_cpu_affinity,
292 thread_context_set_cpu_affinity, NULL, NULL);
293 object_class_property_add(oc, "node-affinity", "int", NULL,
294 thread_context_set_node_affinity, NULL, NULL);
295}
296
297static void thread_context_instance_init(Object *obj)
298{
299 ThreadContext *tc = THREAD_CONTEXT(obj);
300
301 tc->thread_id = -1;
302 qemu_sem_init(&tc->sem, 0);
303 qemu_sem_init(&tc->sem_thread, 0);
304 qemu_mutex_init(&tc->mutex);
305}
306
307static void thread_context_instance_finalize(Object *obj)
308{
309 ThreadContext *tc = THREAD_CONTEXT(obj);
310
311 if (tc->thread_id != -1) {
312 tc->thread_cmd = TC_CMD_STOP;
313 qemu_sem_post(&tc->sem_thread);
314 qemu_thread_join(&tc->thread);
315 }
316 qemu_sem_destroy(&tc->sem);
317 qemu_sem_destroy(&tc->sem_thread);
318 qemu_mutex_destroy(&tc->mutex);
319}
320
321static const TypeInfo thread_context_info = {
322 .name = TYPE_THREAD_CONTEXT,
323 .parent = TYPE_OBJECT,
324 .class_init = thread_context_class_init,
325 .instance_size = sizeof(ThreadContext),
326 .instance_init = thread_context_instance_init,
327 .instance_finalize = thread_context_instance_finalize,
328 .interfaces = (InterfaceInfo[]) {
329 { TYPE_USER_CREATABLE },
330 { }
331 }
332};
333
334static void thread_context_register_types(void)
335{
336 type_register_static(&thread_context_info);
337}
338type_init(thread_context_register_types)
339
340void thread_context_create_thread(ThreadContext *tc, QemuThread *thread,
341 const char *name,
342 void *(*start_routine)(void *), void *arg,
343 int mode)
344{
345 ThreadContextCmdNew data = {
346 .thread = thread,
347 .name = name,
348 .start_routine = start_routine,
349 .arg = arg,
350 .mode = mode,
351 };
352
353 qemu_mutex_lock(&tc->mutex);
354 tc->thread_cmd = TC_CMD_NEW;
355 tc->thread_cmd_data = &data;
356 qemu_sem_post(&tc->sem_thread);
357
358 while (tc->thread_cmd != TC_CMD_NONE) {
359 qemu_sem_wait(&tc->sem);
360 }
361 qemu_mutex_unlock(&tc->mutex);
362}
363