mirror of
				https://github.com/mborgerson/xemu.git
				synced 2025-10-30 04:31:19 +00:00 
			
		
		
		
	Commit 012d4c96e2 changed the visitor functions taking Error ** to return bool instead of void, and the commits following it used the new return value to simplify error checking. Since then a few more uses in need of the same treatment crept in. Do that. All pretty mechanical except for * balloon_stats_get_all() This is basically the same transformation commit 012d4c96e2 applied to the virtual walk example in include/qapi/visitor.h. * set_max_queue_size() Additionally replace "goto end of function" by return. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20221121085054.683122-10-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
		
			
				
	
	
		
			357 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU Thread Context
 | |
|  *
 | |
|  * Copyright Red Hat Inc., 2022
 | |
|  *
 | |
|  * Authors:
 | |
|  *  David Hildenbrand <david@redhat.com>
 | |
|  *
 | |
|  * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | |
|  * See the COPYING file in the top-level directory.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qemu/thread-context.h"
 | |
| #include "qapi/error.h"
 | |
| #include "qapi/qapi-builtin-visit.h"
 | |
| #include "qapi/visitor.h"
 | |
| #include "qemu/config-file.h"
 | |
| #include "qapi/qapi-builtin-visit.h"
 | |
| #include "qom/object_interfaces.h"
 | |
| #include "qemu/module.h"
 | |
| #include "qemu/bitmap.h"
 | |
| 
 | |
| #ifdef CONFIG_NUMA
 | |
| #include <numa.h>
 | |
| #endif
 | |
| 
 | |
| enum {
 | |
|     TC_CMD_NONE = 0,
 | |
|     TC_CMD_STOP,
 | |
|     TC_CMD_NEW,
 | |
| };
 | |
| 
 | |
| typedef struct ThreadContextCmdNew {
 | |
|     QemuThread *thread;
 | |
|     const char *name;
 | |
|     void *(*start_routine)(void *);
 | |
|     void *arg;
 | |
|     int mode;
 | |
| } ThreadContextCmdNew;
 | |
| 
 | |
| static void *thread_context_run(void *opaque)
 | |
| {
 | |
|     ThreadContext *tc = opaque;
 | |
| 
 | |
|     tc->thread_id = qemu_get_thread_id();
 | |
|     qemu_sem_post(&tc->sem);
 | |
| 
 | |
|     while (true) {
 | |
|         /*
 | |
|          * Threads inherit the CPU affinity of the creating thread. For this
 | |
|          * reason, we create new (especially short-lived) threads from our
 | |
|          * persistent context thread.
 | |
|          *
 | |
|          * Especially when QEMU is not allowed to set the affinity itself,
 | |
|          * management tools can simply set the affinity of the context thread
 | |
|          * after creating the context, to have new threads created via
 | |
|          * the context inherit the CPU affinity automatically.
 | |
|          */
 | |
|         switch (tc->thread_cmd) {
 | |
|         case TC_CMD_NONE:
 | |
|             break;
 | |
|         case TC_CMD_STOP:
 | |
|             tc->thread_cmd = TC_CMD_NONE;
 | |
|             qemu_sem_post(&tc->sem);
 | |
|             return NULL;
 | |
|         case TC_CMD_NEW: {
 | |
|             ThreadContextCmdNew *cmd_new = tc->thread_cmd_data;
 | |
| 
 | |
|             qemu_thread_create(cmd_new->thread, cmd_new->name,
 | |
|                                cmd_new->start_routine, cmd_new->arg,
 | |
|                                cmd_new->mode);
 | |
|             tc->thread_cmd = TC_CMD_NONE;
 | |
|             tc->thread_cmd_data = NULL;
 | |
|             qemu_sem_post(&tc->sem);
 | |
|             break;
 | |
|         }
 | |
|         default:
 | |
|             g_assert_not_reached();
 | |
|         }
 | |
|         qemu_sem_wait(&tc->sem_thread);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void thread_context_set_cpu_affinity(Object *obj, Visitor *v,
 | |
|                                             const char *name, void *opaque,
 | |
|                                             Error **errp)
 | |
| {
 | |
|     ThreadContext *tc = THREAD_CONTEXT(obj);
 | |
|     uint16List *l, *host_cpus = NULL;
 | |
|     unsigned long *bitmap = NULL;
 | |
|     int nbits = 0, ret;
 | |
| 
 | |
|     if (tc->init_cpu_bitmap) {
 | |
|         error_setg(errp, "Mixing CPU and node affinity not supported");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!visit_type_uint16List(v, name, &host_cpus, errp)) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!host_cpus) {
 | |
|         error_setg(errp, "CPU list is empty");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     for (l = host_cpus; l; l = l->next) {
 | |
|         nbits = MAX(nbits, l->value + 1);
 | |
|     }
 | |
|     bitmap = bitmap_new(nbits);
 | |
|     for (l = host_cpus; l; l = l->next) {
 | |
|         set_bit(l->value, bitmap);
 | |
|     }
 | |
| 
 | |
|     if (tc->thread_id != -1) {
 | |
|         /*
 | |
|          * Note: we won't be adjusting the affinity of any thread that is still
 | |
|          * around, but only the affinity of the context thread.
 | |
|          */
 | |
|         ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits);
 | |
|         if (ret) {
 | |
|             error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
 | |
|         }
 | |
|     } else {
 | |
|         tc->init_cpu_bitmap = bitmap;
 | |
|         bitmap = NULL;
 | |
|         tc->init_cpu_nbits = nbits;
 | |
|     }
 | |
| out:
 | |
|     g_free(bitmap);
 | |
|     qapi_free_uint16List(host_cpus);
 | |
| }
 | |
| 
 | |
| static void thread_context_get_cpu_affinity(Object *obj, Visitor *v,
 | |
|                                             const char *name, void *opaque,
 | |
|                                             Error **errp)
 | |
| {
 | |
|     unsigned long *bitmap, nbits, value;
 | |
|     ThreadContext *tc = THREAD_CONTEXT(obj);
 | |
|     uint16List *host_cpus = NULL;
 | |
|     uint16List **tail = &host_cpus;
 | |
|     int ret;
 | |
| 
 | |
|     if (tc->thread_id == -1) {
 | |
|         error_setg(errp, "Object not initialized yet");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     ret = qemu_thread_get_affinity(&tc->thread, &bitmap, &nbits);
 | |
|     if (ret) {
 | |
|         error_setg(errp, "Getting CPU affinity failed: %s", strerror(ret));
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     value = find_first_bit(bitmap, nbits);
 | |
|     while (value < nbits) {
 | |
|         QAPI_LIST_APPEND(tail, value);
 | |
| 
 | |
|         value = find_next_bit(bitmap, nbits, value + 1);
 | |
|     }
 | |
|     g_free(bitmap);
 | |
| 
 | |
|     visit_type_uint16List(v, name, &host_cpus, errp);
 | |
|     qapi_free_uint16List(host_cpus);
 | |
| }
 | |
| 
 | |
| static void thread_context_set_node_affinity(Object *obj, Visitor *v,
 | |
|                                              const char *name, void *opaque,
 | |
|                                              Error **errp)
 | |
| {
 | |
| #ifdef CONFIG_NUMA
 | |
|     const int nbits = numa_num_possible_cpus();
 | |
|     ThreadContext *tc = THREAD_CONTEXT(obj);
 | |
|     uint16List *l, *host_nodes = NULL;
 | |
|     unsigned long *bitmap = NULL;
 | |
|     struct bitmask *tmp_cpus;
 | |
|     int ret, i;
 | |
| 
 | |
|     if (tc->init_cpu_bitmap) {
 | |
|         error_setg(errp, "Mixing CPU and node affinity not supported");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!visit_type_uint16List(v, name, &host_nodes, errp)) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!host_nodes) {
 | |
|         error_setg(errp, "Node list is empty");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     bitmap = bitmap_new(nbits);
 | |
|     tmp_cpus = numa_allocate_cpumask();
 | |
|     for (l = host_nodes; l; l = l->next) {
 | |
|         numa_bitmask_clearall(tmp_cpus);
 | |
|         ret = numa_node_to_cpus(l->value, tmp_cpus);
 | |
|         if (ret) {
 | |
|             /* We ignore any errors, such as impossible nodes. */
 | |
|             continue;
 | |
|         }
 | |
|         for (i = 0; i < nbits; i++) {
 | |
|             if (numa_bitmask_isbitset(tmp_cpus, i)) {
 | |
|                 set_bit(i, bitmap);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     numa_free_cpumask(tmp_cpus);
 | |
| 
 | |
|     if (bitmap_empty(bitmap, nbits)) {
 | |
|         error_setg(errp, "The nodes select no CPUs");
 | |
|         goto out;
 | |
|     }
 | |
| 
 | |
|     if (tc->thread_id != -1) {
 | |
|         /*
 | |
|          * Note: we won't be adjusting the affinity of any thread that is still
 | |
|          * around for now, but only the affinity of the context thread.
 | |
|          */
 | |
|         ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits);
 | |
|         if (ret) {
 | |
|             error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
 | |
|         }
 | |
|     } else {
 | |
|         tc->init_cpu_bitmap = bitmap;
 | |
|         bitmap = NULL;
 | |
|         tc->init_cpu_nbits = nbits;
 | |
|     }
 | |
| out:
 | |
|     g_free(bitmap);
 | |
|     qapi_free_uint16List(host_nodes);
 | |
| #else
 | |
|     error_setg(errp, "NUMA node affinity is not supported by this QEMU");
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static void thread_context_get_thread_id(Object *obj, Visitor *v,
 | |
|                                          const char *name, void *opaque,
 | |
|                                          Error **errp)
 | |
| {
 | |
|     ThreadContext *tc = THREAD_CONTEXT(obj);
 | |
|     uint64_t value = tc->thread_id;
 | |
| 
 | |
|     visit_type_uint64(v, name, &value, errp);
 | |
| }
 | |
| 
 | |
| static void thread_context_instance_complete(UserCreatable *uc, Error **errp)
 | |
| {
 | |
|     ThreadContext *tc = THREAD_CONTEXT(uc);
 | |
|     char *thread_name;
 | |
|     int ret;
 | |
| 
 | |
|     thread_name = g_strdup_printf("TC %s",
 | |
|                                object_get_canonical_path_component(OBJECT(uc)));
 | |
|     qemu_thread_create(&tc->thread, thread_name, thread_context_run, tc,
 | |
|                        QEMU_THREAD_JOINABLE);
 | |
|     g_free(thread_name);
 | |
| 
 | |
|     /* Wait until initialization of the thread is done. */
 | |
|     while (tc->thread_id == -1) {
 | |
|         qemu_sem_wait(&tc->sem);
 | |
|     }
 | |
| 
 | |
|     if (tc->init_cpu_bitmap) {
 | |
|         ret = qemu_thread_set_affinity(&tc->thread, tc->init_cpu_bitmap,
 | |
|                                        tc->init_cpu_nbits);
 | |
|         if (ret) {
 | |
|             error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
 | |
|         }
 | |
|         g_free(tc->init_cpu_bitmap);
 | |
|         tc->init_cpu_bitmap = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void thread_context_class_init(ObjectClass *oc, void *data)
 | |
| {
 | |
|     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
 | |
| 
 | |
|     ucc->complete = thread_context_instance_complete;
 | |
|     object_class_property_add(oc, "thread-id", "int",
 | |
|                               thread_context_get_thread_id, NULL, NULL,
 | |
|                               NULL);
 | |
|     object_class_property_add(oc, "cpu-affinity", "int",
 | |
|                               thread_context_get_cpu_affinity,
 | |
|                               thread_context_set_cpu_affinity, NULL, NULL);
 | |
|     object_class_property_add(oc, "node-affinity", "int", NULL,
 | |
|                               thread_context_set_node_affinity, NULL, NULL);
 | |
| }
 | |
| 
 | |
| static void thread_context_instance_init(Object *obj)
 | |
| {
 | |
|     ThreadContext *tc = THREAD_CONTEXT(obj);
 | |
| 
 | |
|     tc->thread_id = -1;
 | |
|     qemu_sem_init(&tc->sem, 0);
 | |
|     qemu_sem_init(&tc->sem_thread, 0);
 | |
|     qemu_mutex_init(&tc->mutex);
 | |
| }
 | |
| 
 | |
| static void thread_context_instance_finalize(Object *obj)
 | |
| {
 | |
|     ThreadContext *tc = THREAD_CONTEXT(obj);
 | |
| 
 | |
|     if (tc->thread_id != -1) {
 | |
|         tc->thread_cmd = TC_CMD_STOP;
 | |
|         qemu_sem_post(&tc->sem_thread);
 | |
|         qemu_thread_join(&tc->thread);
 | |
|     }
 | |
|     qemu_sem_destroy(&tc->sem);
 | |
|     qemu_sem_destroy(&tc->sem_thread);
 | |
|     qemu_mutex_destroy(&tc->mutex);
 | |
| }
 | |
| 
 | |
| static const TypeInfo thread_context_info = {
 | |
|     .name = TYPE_THREAD_CONTEXT,
 | |
|     .parent = TYPE_OBJECT,
 | |
|     .class_init = thread_context_class_init,
 | |
|     .instance_size = sizeof(ThreadContext),
 | |
|     .instance_init = thread_context_instance_init,
 | |
|     .instance_finalize = thread_context_instance_finalize,
 | |
|     .interfaces = (InterfaceInfo[]) {
 | |
|         { TYPE_USER_CREATABLE },
 | |
|         { }
 | |
|     }
 | |
| };
 | |
| 
 | |
| static void thread_context_register_types(void)
 | |
| {
 | |
|     type_register_static(&thread_context_info);
 | |
| }
 | |
| type_init(thread_context_register_types)
 | |
| 
 | |
| void thread_context_create_thread(ThreadContext *tc, QemuThread *thread,
 | |
|                                   const char *name,
 | |
|                                   void *(*start_routine)(void *), void *arg,
 | |
|                                   int mode)
 | |
| {
 | |
|     ThreadContextCmdNew data = {
 | |
|         .thread = thread,
 | |
|         .name = name,
 | |
|         .start_routine = start_routine,
 | |
|         .arg = arg,
 | |
|         .mode = mode,
 | |
|     };
 | |
| 
 | |
|     qemu_mutex_lock(&tc->mutex);
 | |
|     tc->thread_cmd = TC_CMD_NEW;
 | |
|     tc->thread_cmd_data = &data;
 | |
|     qemu_sem_post(&tc->sem_thread);
 | |
| 
 | |
|     while (tc->thread_cmd != TC_CMD_NONE) {
 | |
|         qemu_sem_wait(&tc->sem);
 | |
|     }
 | |
|     qemu_mutex_unlock(&tc->mutex);
 | |
| }
 |