X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=iothread.c;h=7130be58e3e6c83640b7cf03b233c5069568e25b;hb=HEAD;hp=2ec5a3bffea4b8b4e493502970bf3471a80ad8be;hpb=4a22592e32868db846898831c27fbee8e73e24e3;p=mirror_qemu.git diff --git a/iothread.c b/iothread.c index 2ec5a3bffe..e1e9e04736 100644 --- a/iothread.c +++ b/iothread.c @@ -1,7 +1,7 @@ /* * Event loop thread * - * Copyright Red Hat Inc., 2013 + * Copyright Red Hat Inc., 2013, 2020 * * Authors: * Stefan Hajnoczi @@ -17,6 +17,7 @@ #include "qemu/module.h" #include "block/aio.h" #include "block/block.h" +#include "sysemu/event-loop-base.h" #include "sysemu/iothread.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" @@ -24,57 +25,53 @@ #include "qemu/rcu.h" #include "qemu/main-loop.h" -typedef ObjectClass IOThreadClass; - -#define IOTHREAD_GET_CLASS(obj) \ - OBJECT_GET_CLASS(IOThreadClass, obj, TYPE_IOTHREAD) -#define IOTHREAD_CLASS(klass) \ - OBJECT_CLASS_CHECK(IOThreadClass, klass, TYPE_IOTHREAD) +#ifdef CONFIG_POSIX /* Benchmark results from 2016 on NVMe SSD drives show max polling times around * 16-32 microseconds yield IOPS improvements for both iodepth=1 and iodepth=32 * workloads. */ #define IOTHREAD_POLL_MAX_NS_DEFAULT 32768ULL - -static __thread IOThread *my_iothread; - -AioContext *qemu_get_current_aio_context(void) -{ - return my_iothread ? my_iothread->ctx : qemu_get_aio_context(); -} +#else +#define IOTHREAD_POLL_MAX_NS_DEFAULT 0ULL +#endif static void *iothread_run(void *opaque) { IOThread *iothread = opaque; rcu_register_thread(); - - my_iothread = iothread; - qemu_mutex_lock(&iothread->init_done_lock); + /* + * g_main_context_push_thread_default() must be called before anything + * in this new thread uses glib. + */ + g_main_context_push_thread_default(iothread->worker_context); + qemu_set_current_aio_context(iothread->ctx); iothread->thread_id = qemu_get_thread_id(); - qemu_cond_signal(&iothread->init_done_cond); - qemu_mutex_unlock(&iothread->init_done_lock); + qemu_sem_post(&iothread->init_done_sem); while (iothread->running) { + /* + * Note: from functional-wise the g_main_loop_run() below can + * already cover the aio_poll() events, but we can't run the + * main loop unconditionally because explicit aio_poll() here + * is faster than g_main_loop_run() when we do not need the + * gcontext at all (e.g., pure block layer iothreads). In + * other words, when we want to run the gcontext with the + * iothread we need to pay some performance for functionality. + */ aio_poll(iothread->ctx, true); - if (atomic_read(&iothread->worker_context)) { - GMainLoop *loop; - - g_main_context_push_thread_default(iothread->worker_context); - iothread->main_loop = - g_main_loop_new(iothread->worker_context, TRUE); - loop = iothread->main_loop; - + /* + * We must check the running state again in case it was + * changed in previous aio_poll() + */ + if (iothread->running && qatomic_read(&iothread->run_gcontext)) { g_main_loop_run(iothread->main_loop); - iothread->main_loop = NULL; - g_main_loop_unref(loop); - - g_main_context_pop_thread_default(iothread->worker_context); } } + g_main_context_pop_thread_default(iothread->worker_context); rcu_unregister_thread(); return NULL; } @@ -101,23 +98,15 @@ void iothread_stop(IOThread *iothread) qemu_thread_join(&iothread->thread); } -static int iothread_stop_iter(Object *object, void *opaque) -{ - IOThread *iothread; - - iothread = (IOThread *)object_dynamic_cast(object, TYPE_IOTHREAD); - if (!iothread) { - return 0; - } - iothread_stop(iothread); - return 0; -} - static void iothread_instance_init(Object *obj) { IOThread *iothread = IOTHREAD(obj); iothread->poll_max_ns = IOTHREAD_POLL_MAX_NS_DEFAULT; + iothread->thread_id = -1; + qemu_sem_init(&iothread->init_done_sem, 0); + /* By default, we don't run gcontext */ + qatomic_set(&iothread->run_gcontext, 0); } static void iothread_instance_finalize(Object *obj) @@ -125,38 +114,92 @@ static void iothread_instance_finalize(Object *obj) IOThread *iothread = IOTHREAD(obj); iothread_stop(iothread); + + /* + * Before glib2 2.33.10, there is a glib2 bug that GSource context + * pointer may not be cleared even if the context has already been + * destroyed (while it should). Here let's free the AIO context + * earlier to bypass that glib bug. + * + * We can remove this comment after the minimum supported glib2 + * version boosts to 2.33.10. Before that, let's free the + * GSources first before destroying any GMainContext. + */ + if (iothread->ctx) { + aio_context_unref(iothread->ctx); + iothread->ctx = NULL; + } if (iothread->worker_context) { g_main_context_unref(iothread->worker_context); iothread->worker_context = NULL; + g_main_loop_unref(iothread->main_loop); + iothread->main_loop = NULL; } - qemu_cond_destroy(&iothread->init_done_cond); - qemu_mutex_destroy(&iothread->init_done_lock); + qemu_sem_destroy(&iothread->init_done_sem); +} + +static void iothread_init_gcontext(IOThread *iothread, const char *thread_name) +{ + GSource *source; + g_autofree char *name = g_strdup_printf("%s aio-context", thread_name); + + iothread->worker_context = g_main_context_new(); + source = aio_get_g_source(iothread_get_aio_context(iothread)); + g_source_set_name(source, name); + g_source_attach(source, iothread->worker_context); + g_source_unref(source); + iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE); +} + +static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp) +{ + ERRP_GUARD(); + IOThread *iothread = IOTHREAD(base); + if (!iothread->ctx) { return; } - aio_context_unref(iothread->ctx); + + aio_context_set_poll_params(iothread->ctx, + iothread->poll_max_ns, + iothread->poll_grow, + iothread->poll_shrink, + errp); + if (*errp) { + return; + } + + aio_context_set_aio_params(iothread->ctx, + iothread->parent_obj.aio_max_batch); + + aio_context_set_thread_pool_params(iothread->ctx, base->thread_pool_min, + base->thread_pool_max, errp); } -static void iothread_complete(UserCreatable *obj, Error **errp) + +static void iothread_init(EventLoopBase *base, Error **errp) { Error *local_error = NULL; - IOThread *iothread = IOTHREAD(obj); - char *name, *thread_name; + IOThread *iothread = IOTHREAD(base); + g_autofree char *thread_name = NULL; iothread->stopping = false; iothread->running = true; - iothread->thread_id = -1; - iothread->ctx = aio_context_new(&local_error); + iothread->ctx = aio_context_new(errp); if (!iothread->ctx) { - error_propagate(errp, local_error); return; } - aio_context_set_poll_params(iothread->ctx, - iothread->poll_max_ns, - iothread->poll_grow, - iothread->poll_shrink, - &local_error); + thread_name = g_strdup_printf("IO %s", + object_get_canonical_path_component(OBJECT(base))); + + /* + * Init one GMainContext for the iothread unconditionally, even if + * it's not used + */ + iothread_init_gcontext(iothread, thread_name); + + iothread_set_aio_context_params(base, &local_error); if (local_error) { error_propagate(errp, local_error); aio_context_unref(iothread->ctx); @@ -164,118 +207,119 @@ static void iothread_complete(UserCreatable *obj, Error **errp) return; } - qemu_mutex_init(&iothread->init_done_lock); - qemu_cond_init(&iothread->init_done_cond); - iothread->once = (GOnce) G_ONCE_INIT; - /* This assumes we are called from a thread with useful CPU affinity for us * to inherit. */ - name = object_get_canonical_path_component(OBJECT(obj)); - thread_name = g_strdup_printf("IO %s", name); qemu_thread_create(&iothread->thread, thread_name, iothread_run, iothread, QEMU_THREAD_JOINABLE); - g_free(thread_name); - g_free(name); /* Wait for initialization to complete */ - qemu_mutex_lock(&iothread->init_done_lock); while (iothread->thread_id == -1) { - qemu_cond_wait(&iothread->init_done_cond, - &iothread->init_done_lock); + qemu_sem_wait(&iothread->init_done_sem); } - qemu_mutex_unlock(&iothread->init_done_lock); } typedef struct { const char *name; ptrdiff_t offset; /* field's byte offset in IOThread struct */ -} PollParamInfo; +} IOThreadParamInfo; -static PollParamInfo poll_max_ns_info = { +static IOThreadParamInfo poll_max_ns_info = { "poll-max-ns", offsetof(IOThread, poll_max_ns), }; -static PollParamInfo poll_grow_info = { +static IOThreadParamInfo poll_grow_info = { "poll-grow", offsetof(IOThread, poll_grow), }; -static PollParamInfo poll_shrink_info = { +static IOThreadParamInfo poll_shrink_info = { "poll-shrink", offsetof(IOThread, poll_shrink), }; -static void iothread_get_poll_param(Object *obj, Visitor *v, - const char *name, void *opaque, Error **errp) +static void iothread_get_param(Object *obj, Visitor *v, + const char *name, IOThreadParamInfo *info, Error **errp) { IOThread *iothread = IOTHREAD(obj); - PollParamInfo *info = opaque; int64_t *field = (void *)iothread + info->offset; visit_type_int64(v, name, field, errp); } -static void iothread_set_poll_param(Object *obj, Visitor *v, - const char *name, void *opaque, Error **errp) +static bool iothread_set_param(Object *obj, Visitor *v, + const char *name, IOThreadParamInfo *info, Error **errp) { IOThread *iothread = IOTHREAD(obj); - PollParamInfo *info = opaque; int64_t *field = (void *)iothread + info->offset; - Error *local_err = NULL; int64_t value; - visit_type_int64(v, name, &value, &local_err); - if (local_err) { - goto out; + if (!visit_type_int64(v, name, &value, errp)) { + return false; } if (value < 0) { - error_setg(&local_err, "%s value must be in range [0, %"PRId64"]", + error_setg(errp, "%s value must be in range [0, %" PRId64 "]", info->name, INT64_MAX); - goto out; + return false; } *field = value; + return true; +} + +static void iothread_get_poll_param(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + IOThreadParamInfo *info = opaque; + + iothread_get_param(obj, v, name, info, errp); +} + +static void iothread_set_poll_param(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + IOThread *iothread = IOTHREAD(obj); + IOThreadParamInfo *info = opaque; + + if (!iothread_set_param(obj, v, name, info, errp)) { + return; + } + if (iothread->ctx) { aio_context_set_poll_params(iothread->ctx, iothread->poll_max_ns, iothread->poll_grow, iothread->poll_shrink, - &local_err); + errp); } - -out: - error_propagate(errp, local_err); } static void iothread_class_init(ObjectClass *klass, void *class_data) { - UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); - ucc->complete = iothread_complete; + EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass); + + bc->init = iothread_init; + bc->update_params = iothread_set_aio_context_params; object_class_property_add(klass, "poll-max-ns", "int", iothread_get_poll_param, iothread_set_poll_param, - NULL, &poll_max_ns_info, &error_abort); + NULL, &poll_max_ns_info); object_class_property_add(klass, "poll-grow", "int", iothread_get_poll_param, iothread_set_poll_param, - NULL, &poll_grow_info, &error_abort); + NULL, &poll_grow_info); object_class_property_add(klass, "poll-shrink", "int", iothread_get_poll_param, iothread_set_poll_param, - NULL, &poll_shrink_info, &error_abort); + NULL, &poll_shrink_info); } static const TypeInfo iothread_info = { .name = TYPE_IOTHREAD, - .parent = TYPE_OBJECT, + .parent = TYPE_EVENT_LOOP_BASE, .class_init = iothread_class_init, .instance_size = sizeof(IOThread), .instance_init = iothread_instance_init, .instance_finalize = iothread_instance_finalize, - .interfaces = (InterfaceInfo[]) { - {TYPE_USER_CREATABLE}, - {} - }, }; static void iothread_register_types(void) @@ -287,7 +331,7 @@ type_init(iothread_register_types) char *iothread_get_id(IOThread *iothread) { - return object_get_canonical_path_component(OBJECT(iothread)); + return g_strdup(object_get_canonical_path_component(OBJECT(iothread))); } AioContext *iothread_get_aio_context(IOThread *iothread) @@ -297,8 +341,7 @@ AioContext *iothread_get_aio_context(IOThread *iothread) static int query_one_iothread(Object *object, void *opaque) { - IOThreadInfoList ***prev = opaque; - IOThreadInfoList *elem; + IOThreadInfoList ***tail = opaque; IOThreadInfo *info; IOThread *iothread; @@ -313,13 +356,9 @@ static int query_one_iothread(Object *object, void *opaque) info->poll_max_ns = iothread->poll_max_ns; info->poll_grow = iothread->poll_grow; info->poll_shrink = iothread->poll_shrink; + info->aio_max_batch = iothread->parent_obj.aio_max_batch; - elem = g_new0(IOThreadInfoList, 1); - elem->value = info; - elem->next = NULL; - - **prev = elem; - *prev = &elem->next; + QAPI_LIST_APPEND(*tail, info); return 0; } @@ -333,46 +372,10 @@ IOThreadInfoList *qmp_query_iothreads(Error **errp) return head; } -void iothread_stop_all(void) -{ - Object *container = object_get_objects_root(); - BlockDriverState *bs; - BdrvNextIterator it; - - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - AioContext *ctx = bdrv_get_aio_context(bs); - if (ctx == qemu_get_aio_context()) { - continue; - } - aio_context_acquire(ctx); - bdrv_set_aio_context(bs, qemu_get_aio_context()); - aio_context_release(ctx); - } - - object_child_foreach(container, iothread_stop_iter, NULL); -} - -static gpointer iothread_g_main_context_init(gpointer opaque) -{ - AioContext *ctx; - IOThread *iothread = opaque; - GSource *source; - - iothread->worker_context = g_main_context_new(); - - ctx = iothread_get_aio_context(iothread); - source = aio_get_g_source(ctx); - g_source_attach(source, iothread->worker_context); - g_source_unref(source); - - aio_notify(iothread->ctx); - return NULL; -} - GMainContext *iothread_get_g_main_context(IOThread *iothread) { - g_once(&iothread->once, iothread_g_main_context_init, iothread); - + qatomic_set(&iothread->run_gcontext, 1); + aio_notify(iothread->ctx); return iothread->worker_context; } @@ -398,3 +401,8 @@ IOThread *iothread_by_id(const char *id) { return IOTHREAD(object_resolve_path_type(id, TYPE_IOTHREAD, NULL)); } + +bool qemu_in_iothread(void) +{ + return qemu_get_current_aio_context() != qemu_get_aio_context(); +}