qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

thread-context.c (10538B)


      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     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          * Note: we won't be adjusting the affinity of any thread that is still
    122          * around, but only the affinity of the context thread.
    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     }
    133 out:
    134     g_free(bitmap);
    135     qapi_free_uint16List(host_cpus);
    136 }
    137 
    138 static 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 
    171 static 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             /* We ignore any errors, such as impossible nodes. */
    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          * Note: we won't be adjusting the affinity of any thread that is still
    225          * around for now, but only the affinity of the context thread.
    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     }
    236 out:
    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 
    244 static 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 
    254 static 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     /* Wait until initialization of the thread is done. */
    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 
    282 static 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 
    297 static 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 
    307 static 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 
    321 static 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 
    334 static void thread_context_register_types(void)
    335 {
    336     type_register_static(&thread_context_info);
    337 }
    338 type_init(thread_context_register_types)
    339 
    340 void 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 }