mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-09-06 01:43:39 +00:00
![Nicolas Saenz Julienne](/assets/img/avatar_default.png)
The thread pool regulates itself: when idle, it kills threads until empty, when in demand, it creates new threads until full. This behaviour doesn't play well with latency sensitive workloads where the price of creating a new thread is too high. For example, when paired with qemu's '-mlock', or using safety features like SafeStack, creating a new thread has been measured take multiple milliseconds. In order to mitigate this let's introduce a new 'EventLoopBase' property to set the thread pool size. The threads will be created during the pool's initialization or upon updating the property's value, remain available during its lifetime regardless of demand, and destroyed upon freeing it. A properly characterized workload will then be able to configure the pool to avoid any latency spikes. Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Message-id: 20220425075723.20019-4-nsaenzju@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
141 lines
4 KiB
C
141 lines
4 KiB
C
/*
|
|
* QEMU event-loop base
|
|
*
|
|
* Copyright (C) 2022 Red Hat Inc
|
|
*
|
|
* Authors:
|
|
* Stefan Hajnoczi <stefanha@redhat.com>
|
|
* Nicolas Saenz Julienne <nsaenzju@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 "qom/object_interfaces.h"
|
|
#include "qapi/error.h"
|
|
#include "block/thread-pool.h"
|
|
#include "sysemu/event-loop-base.h"
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
|
|
} EventLoopBaseParamInfo;
|
|
|
|
static void event_loop_base_instance_init(Object *obj)
|
|
{
|
|
EventLoopBase *base = EVENT_LOOP_BASE(obj);
|
|
|
|
base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
|
|
}
|
|
|
|
static EventLoopBaseParamInfo aio_max_batch_info = {
|
|
"aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
|
|
};
|
|
static EventLoopBaseParamInfo thread_pool_min_info = {
|
|
"thread-pool-min", offsetof(EventLoopBase, thread_pool_min),
|
|
};
|
|
static EventLoopBaseParamInfo thread_pool_max_info = {
|
|
"thread-pool-max", offsetof(EventLoopBase, thread_pool_max),
|
|
};
|
|
|
|
static void event_loop_base_get_param(Object *obj, Visitor *v,
|
|
const char *name, void *opaque, Error **errp)
|
|
{
|
|
EventLoopBase *event_loop_base = EVENT_LOOP_BASE(obj);
|
|
EventLoopBaseParamInfo *info = opaque;
|
|
int64_t *field = (void *)event_loop_base + info->offset;
|
|
|
|
visit_type_int64(v, name, field, errp);
|
|
}
|
|
|
|
static void event_loop_base_set_param(Object *obj, Visitor *v,
|
|
const char *name, void *opaque, Error **errp)
|
|
{
|
|
EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(obj);
|
|
EventLoopBase *base = EVENT_LOOP_BASE(obj);
|
|
EventLoopBaseParamInfo *info = opaque;
|
|
int64_t *field = (void *)base + info->offset;
|
|
int64_t value;
|
|
|
|
if (!visit_type_int64(v, name, &value, errp)) {
|
|
return;
|
|
}
|
|
|
|
if (value < 0) {
|
|
error_setg(errp, "%s value must be in range [0, %" PRId64 "]",
|
|
info->name, INT64_MAX);
|
|
return;
|
|
}
|
|
|
|
*field = value;
|
|
|
|
if (bc->update_params) {
|
|
bc->update_params(base, errp);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void event_loop_base_complete(UserCreatable *uc, Error **errp)
|
|
{
|
|
EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
|
|
EventLoopBase *base = EVENT_LOOP_BASE(uc);
|
|
|
|
if (bc->init) {
|
|
bc->init(base, errp);
|
|
}
|
|
}
|
|
|
|
static bool event_loop_base_can_be_deleted(UserCreatable *uc)
|
|
{
|
|
EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
|
|
EventLoopBase *backend = EVENT_LOOP_BASE(uc);
|
|
|
|
if (bc->can_be_deleted) {
|
|
return bc->can_be_deleted(backend);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
|
|
{
|
|
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
|
|
ucc->complete = event_loop_base_complete;
|
|
ucc->can_be_deleted = event_loop_base_can_be_deleted;
|
|
|
|
object_class_property_add(klass, "aio-max-batch", "int",
|
|
event_loop_base_get_param,
|
|
event_loop_base_set_param,
|
|
NULL, &aio_max_batch_info);
|
|
object_class_property_add(klass, "thread-pool-min", "int",
|
|
event_loop_base_get_param,
|
|
event_loop_base_set_param,
|
|
NULL, &thread_pool_min_info);
|
|
object_class_property_add(klass, "thread-pool-max", "int",
|
|
event_loop_base_get_param,
|
|
event_loop_base_set_param,
|
|
NULL, &thread_pool_max_info);
|
|
}
|
|
|
|
static const TypeInfo event_loop_base_info = {
|
|
.name = TYPE_EVENT_LOOP_BASE,
|
|
.parent = TYPE_OBJECT,
|
|
.instance_size = sizeof(EventLoopBase),
|
|
.instance_init = event_loop_base_instance_init,
|
|
.class_size = sizeof(EventLoopBaseClass),
|
|
.class_init = event_loop_base_class_init,
|
|
.abstract = true,
|
|
.interfaces = (InterfaceInfo[]) {
|
|
{ TYPE_USER_CREATABLE },
|
|
{ }
|
|
}
|
|
};
|
|
|
|
static void register_types(void)
|
|
{
|
|
type_register_static(&event_loop_base_info);
|
|
}
|
|
type_init(register_types);
|