qemu

Форк
0
/
thread-context.c 
356 строк · 10.2 Кб
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

24
#ifdef CONFIG_NUMA
25
#include <numa.h>
26
#endif
27

28
enum {
29
    TC_CMD_NONE = 0,
30
    TC_CMD_STOP,
31
    TC_CMD_NEW,
32
};
33

34
typedef struct ThreadContextCmdNew {
35
    QemuThread *thread;
36
    const char *name;
37
    void *(*start_routine)(void *);
38
    void *arg;
39
    int mode;
40
} ThreadContextCmdNew;
41

42
static 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

85
static 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

94
    if (tc->init_cpu_bitmap) {
95
        error_setg(errp, "Mixing CPU and node affinity not supported");
96
        return;
97
    }
98

99
    if (!visit_type_uint16List(v, name, &host_cpus, errp)) {
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
    }
130
out:
131
    g_free(bitmap);
132
    qapi_free_uint16List(host_cpus);
133
}
134

135
static 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

168
static 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;
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

185
    if (!visit_type_uint16List(v, name, &host_nodes, errp)) {
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
    }
230
out:
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

238
static 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

248
static 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

276
static 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);
287
    object_class_property_add(oc, "node-affinity", "int", NULL,
288
                              thread_context_set_node_affinity, NULL, NULL);
289
}
290

291
static 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

301
static 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

315
static 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

328
static void thread_context_register_types(void)
329
{
330
    type_register_static(&thread_context_info);
331
}
332
type_init(thread_context_register_types)
333

334
void 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
}
357

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.