aboutsummaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2022-05-09 11:07:04 -0700
committerRichard Henderson <richard.henderson@linaro.org>2022-05-09 11:07:04 -0700
commit178bacb66d98d9ee7a702b9f2a4dfcd88b72a9ab (patch)
treeab363c70e86292f400bea30bcf207eac414d80be /util
parentb0c3c60366ed43eb1569eb18c10df6eb993534c3 (diff)
parent3dc584abeef0e1277c2de8c1c1974cb49444eb0a (diff)
downloadqemu-178bacb66d98d9ee7a702b9f2a4dfcd88b72a9ab.zip
qemu-178bacb66d98d9ee7a702b9f2a4dfcd88b72a9ab.tar.gz
qemu-178bacb66d98d9ee7a702b9f2a4dfcd88b72a9ab.tar.bz2
Merge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging
Pull request - Add new thread-pool-min/thread-pool-max parameters to control the thread pool used for async I/O. - Fix virtio-scsi IOThread 100% CPU consumption QEMU 7.0 regression. # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCAAdFiEEhpWov9P5fNqsNXdanKSrs4Grc8gFAmJ5DqgACgkQnKSrs4Gr # c8iAqAf/WEJzEso0Hu3UUYJi2lAXpLxWPjoNBlPdQlKIJ/I0zQIF0P7GeCifF+0l # iMjgBv0ofyAuV47gaTJlVrAR75+hJ/IXNDhnu3UuvNWfVOqvksgw6kuHkMo9A2hC # 4tIHEU9J8jbQSSdQTaZR8Zj4FX1/zcxMBAXT3YO3De6zo78RatBTuNP4dsZzt8bI # Qs1a4A0p2ScNXK8EcF4QwAWfoxu9OPPzN52DBCNxcIcnn0SUab4NbDxzpRV4ZhDP # 08WoafI5O+2Kb36QysJN01LqajHrClG/fozrPzBLq5aZUK3xewJGB1hEdGTLkkmz # NJNBg5Ldszwj4PDZ1dFU3/03aigb3g== # =t5eR # -----END PGP SIGNATURE----- # gpg: Signature made Mon 09 May 2022 05:52:56 AM PDT # gpg: using RSA key 8695A8BFD3F97CDAAC35775A9CA4ABB381AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" [full] # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" [full] * tag 'block-pull-request' of https://gitlab.com/stefanha/qemu: virtio-scsi: move request-related items from .h to .c virtio-scsi: clean up virtio_scsi_handle_cmd_vq() virtio-scsi: clean up virtio_scsi_handle_ctrl_vq() virtio-scsi: clean up virtio_scsi_handle_event_vq() virtio-scsi: don't waste CPU polling the event virtqueue virtio-scsi: fix ctrl and event handler functions in dataplane mode util/event-loop-base: Introduce options to set the thread pool size util/main-loop: Introduce the main loop into QOM Introduce event-loop-base abstract class Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'util')
-rw-r--r--util/aio-posix.c1
-rw-r--r--util/async.c20
-rw-r--r--util/main-loop.c65
-rw-r--r--util/thread-pool.c55
4 files changed, 137 insertions, 4 deletions
diff --git a/util/aio-posix.c b/util/aio-posix.c
index be0182a..731f382 100644
--- a/util/aio-posix.c
+++ b/util/aio-posix.c
@@ -15,6 +15,7 @@
#include "qemu/osdep.h"
#include "block/block.h"
+#include "block/thread-pool.h"
#include "qemu/main-loop.h"
#include "qemu/rcu.h"
#include "qemu/rcu_queue.h"
diff --git a/util/async.c b/util/async.c
index 2ea1172..554ba70 100644
--- a/util/async.c
+++ b/util/async.c
@@ -563,6 +563,9 @@ AioContext *aio_context_new(Error **errp)
ctx->aio_max_batch = 0;
+ ctx->thread_pool_min = 0;
+ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
+
return ctx;
fail:
g_source_destroy(&ctx->source);
@@ -696,3 +699,20 @@ void qemu_set_current_aio_context(AioContext *ctx)
assert(!get_my_aiocontext());
set_my_aiocontext(ctx);
}
+
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
+ int64_t max, Error **errp)
+{
+
+ if (min > max || !max || min > INT_MAX || max > INT_MAX) {
+ error_setg(errp, "bad thread-pool-min/thread-pool-max values");
+ return;
+ }
+
+ ctx->thread_pool_min = min;
+ ctx->thread_pool_max = max;
+
+ if (ctx->thread_pool) {
+ thread_pool_update_params(ctx->thread_pool, ctx);
+ }
+}
diff --git a/util/main-loop.c b/util/main-loop.c
index 9afac10..f00a254 100644
--- a/util/main-loop.c
+++ b/util/main-loop.c
@@ -30,9 +30,11 @@
#include "sysemu/replay.h"
#include "qemu/main-loop.h"
#include "block/aio.h"
+#include "block/thread-pool.h"
#include "qemu/error-report.h"
#include "qemu/queue.h"
#include "qemu/compiler.h"
+#include "qom/object.h"
#ifndef _WIN32
#include <sys/wait.h>
@@ -184,6 +186,69 @@ int qemu_init_main_loop(Error **errp)
return 0;
}
+static void main_loop_update_params(EventLoopBase *base, Error **errp)
+{
+ ERRP_GUARD();
+
+ if (!qemu_aio_context) {
+ error_setg(errp, "qemu aio context not ready");
+ return;
+ }
+
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
+ if (*errp) {
+ return;
+ }
+
+ aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min,
+ base->thread_pool_max, errp);
+}
+
+MainLoop *mloop;
+
+static void main_loop_init(EventLoopBase *base, Error **errp)
+{
+ MainLoop *m = MAIN_LOOP(base);
+
+ if (mloop) {
+ error_setg(errp, "only one main-loop instance allowed");
+ return;
+ }
+
+ main_loop_update_params(base, errp);
+
+ mloop = m;
+ return;
+}
+
+static bool main_loop_can_be_deleted(EventLoopBase *base)
+{
+ return false;
+}
+
+static void main_loop_class_init(ObjectClass *oc, void *class_data)
+{
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc);
+
+ bc->init = main_loop_init;
+ bc->update_params = main_loop_update_params;
+ bc->can_be_deleted = main_loop_can_be_deleted;
+}
+
+static const TypeInfo main_loop_info = {
+ .name = TYPE_MAIN_LOOP,
+ .parent = TYPE_EVENT_LOOP_BASE,
+ .class_init = main_loop_class_init,
+ .instance_size = sizeof(MainLoop),
+};
+
+static void main_loop_register_types(void)
+{
+ type_register_static(&main_loop_info);
+}
+
+type_init(main_loop_register_types)
+
static int max_priority;
#ifndef _WIN32
diff --git a/util/thread-pool.c b/util/thread-pool.c
index d763cea..196835b 100644
--- a/util/thread-pool.c
+++ b/util/thread-pool.c
@@ -58,7 +58,6 @@ struct ThreadPool {
QemuMutex lock;
QemuCond worker_stopped;
QemuSemaphore sem;
- int max_threads;
QEMUBH *new_thread_bh;
/* The following variables are only accessed from one AioContext. */
@@ -71,8 +70,27 @@ struct ThreadPool {
int new_threads; /* backlog of threads we need to create */
int pending_threads; /* threads created but not running yet */
bool stopping;
+ int min_threads;
+ int max_threads;
};
+static inline bool back_to_sleep(ThreadPool *pool, int ret)
+{
+ /*
+ * The semaphore timed out, we should exit the loop except when:
+ * - There is work to do, we raced with the signal.
+ * - The max threads threshold just changed, we raced with the signal.
+ * - The thread pool forces a minimum number of readily available threads.
+ */
+ if (ret == -1 && (!QTAILQ_EMPTY(&pool->request_list) ||
+ pool->cur_threads > pool->max_threads ||
+ pool->cur_threads <= pool->min_threads)) {
+ return true;
+ }
+
+ return false;
+}
+
static void *worker_thread(void *opaque)
{
ThreadPool *pool = opaque;
@@ -91,8 +109,9 @@ static void *worker_thread(void *opaque)
ret = qemu_sem_timedwait(&pool->sem, 10000);
qemu_mutex_lock(&pool->lock);
pool->idle_threads--;
- } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list));
- if (ret == -1 || pool->stopping) {
+ } while (back_to_sleep(pool, ret));
+ if (ret == -1 || pool->stopping ||
+ pool->cur_threads > pool->max_threads) {
break;
}
@@ -294,6 +313,33 @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg)
thread_pool_submit_aio(pool, func, arg, NULL, NULL);
}
+void thread_pool_update_params(ThreadPool *pool, AioContext *ctx)
+{
+ qemu_mutex_lock(&pool->lock);
+
+ pool->min_threads = ctx->thread_pool_min;
+ pool->max_threads = ctx->thread_pool_max;
+
+ /*
+ * We either have to:
+ * - Increase the number available of threads until over the min_threads
+ * threshold.
+ * - Decrease the number of available threads until under the max_threads
+ * threshold.
+ * - Do nothing. The current number of threads fall in between the min and
+ * max thresholds. We'll let the pool manage itself.
+ */
+ for (int i = pool->cur_threads; i < pool->min_threads; i++) {
+ spawn_thread(pool);
+ }
+
+ for (int i = pool->cur_threads; i > pool->max_threads; i--) {
+ qemu_sem_post(&pool->sem);
+ }
+
+ qemu_mutex_unlock(&pool->lock);
+}
+
static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
{
if (!ctx) {
@@ -306,11 +352,12 @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
qemu_mutex_init(&pool->lock);
qemu_cond_init(&pool->worker_stopped);
qemu_sem_init(&pool->sem, 0);
- pool->max_threads = 64;
pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool);
QLIST_INIT(&pool->head);
QTAILQ_INIT(&pool->request_list);
+
+ thread_pool_update_params(pool, ctx);
}
ThreadPool *thread_pool_new(AioContext *ctx)