qemu: Make AIO threading framework generic.
v3 cover qemu: Make AIO threading framework generic.
Hi,
This is the v3 of the patch-series to have a generic asynchronous task offloading framework within qemu.
V2 can be found here:
http://lists.gnu.org/archive/html/qemu-devel/2010-05/msg02227.html
Changes from V2:
=====================================================================
- Made use of the qemu-thread.c wrappers in place of pthread_() calls.
- Added a couple fo qemu-thread wrappers for handling pthread_attr_t type.
- Audited the error handling in the generic asynchronous task offloading framework code.
Description
=====================================================================
This patch series decouples the asynchrnous threading framework implementation from posix-aio-compat.c to implement a generic asynchrnous task offloading threading framework which can be used by other subsystems within QEMU.
Currently within QEMU, the AIO subsystem (paio) creates a bunch of asynchronous threads to offload any blocking operations so that the vcpu threads and the IO thread can go back to servicing any other guest requests.
This offloading framework can be used by subsystems such as virtio-9p, Asynchronous encoding for vnc-server, so that the vcpu thread can offload blocking operations on to the asynchronous threads and resume servicing any other guest requests. The asynchronous threads, after finishing the blocking operations can then transfer the control over to the IO thread so that the latter can handle the post_blocking_operation().
The patch series passed fsstress test without any issues.
Could it be considered for inclusion ?
---
Aneesh Kumar K.V (1):
qemu: Generic asynchronous threading framework to offload tasks
Gautham R Shenoy (2):
qemu: Add qemu-wrappers for pthread_attr_t
qemu: Convert AIO code to use the generic threading infrastructure.
Makefile.objs | 3 + async-work.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++ async-work.h | 85 +++++++++++++++++++++++++++++ posix-aio-compat.c | 155 ++++++++++------------------------------------------ qemu-thread.c | 34 +++++++++++ qemu-thread.h | 11 ++++ 6 files changed, 297 insertions(+), 127 deletions(-) create mode 100644 async-work.c create mode 100644 async-work.h
Signature · 2 lines
Thanks and Regards gautham.
PATCH v3 1/3 qemu: Add qemu-wrappers for pthread_attr_t
Add qemu wrappers for pthread_attr_t handling.
--- qemu-thread.c | 34 ++++++++++++++++++++++++++++++++++ qemu-thread.h | 11 +++++++++++ 2 files changed, 45 insertions(+), 0 deletions(-)
qemu-thread.c +34 -0
@@ -142,6 +142,40 @@ void qemu_thread_create(QemuThread *thread,error_exit (err, __func__); } +void qemu_thread_create_attr (QemuThread *thread,QemuThreadAttr *attr1, +void *(*start_routine)(void *), +void *arg) +{ +int err; + + err =pthread_create (&thread->thread, &(attr1->attr), start_routine, + arg); +if (err) +error_exit (err, __func__); +} + +void qemu_thread_attr_init (QemuThreadAttr *attr1) +{ +int err; + + err =pthread_attr_init (&(attr1->attr)); + +if (err) { +error_exit (err, __func__); + } +} + +void qemu_thread_attr_setdetachstate (QemuThreadAttr *attr1,int detachstate) +{ +int err; + + err =pthread_attr_setdetachstate (&(attr1->attr), detachstate); + +if (err) { +error_exit (err, __func__); + } +} +void qemu_thread_signal (QemuThread *thread,int sig) {int err;
qemu-thread.h +11 -0
@@ -15,9 +15,14 @@ struct QemuThread {pthread_t thread; }; +struct QemuThreadAttr { +pthread_attr_t attr; +}; +typedef struct QemuMutex QemuMutex ;typedef struct QemuCond QemuCond ;typedef struct QemuThread QemuThread ; +typedef struct QemuThreadAttr QemuThreadAttr ;void qemu_mutex_init (QemuMutex *mutex);void qemu_mutex_lock (QemuMutex *mutex);
@@ -34,7 +39,13 @@ int qemu_cond_timedwait(QemuCond *cond, QemuMutex *mutex, uint64_t msecs);void qemu_thread_create (QemuThread *thread,void *(*start_routine)(void *),void *arg); +void qemu_thread_create_attr (QemuThread *thread,QemuThreadAttr *attr1, +void *(*start_routine)(void *), +void *arg);void qemu_thread_signal (QemuThread *thread,int sig);void qemu_thread_self (QemuThread *thread);int qemu_thread_equal (QemuThread *thread1,QemuThread *thread2); + +void qemu_thread_attr_init (QemuThreadAttr *attr1); +void qemu_thread_attr_setdetachstate (QemuThreadAttr *attr1,int detachstate);#endif
[Qemu-devel] Re: [PATCH V3 1/3] qemu: Add qemu-wrappers for pthread_attr_t
Quoted 1 line · Gautham R Shenoy
Add qemu wrappers for
pthread_attr_thandling.
The point of these wrappers AFAIU is not only to add error_exit, but also to be portable to Windows in the future. Is it necessary to create the threads as detached? If you set queue->min_threads to zero all threads should exit as soon as they finish their work (which is better than exiting immediately).
Paolo
Re: [Qemu-devel] Re: [PATCH V3 1/3] qemu: Add qemu-wrappers for pthread_attr_t
Quoted 4 lines · Paolo Bonzini
Quoted 1 line · Gautham R Shenoy
Add qemu wrappers for
pthread_attr_thandling.The point of these wrappers AFAIU is not only to add
error_exit, but also to be portable to Windows in the future. Is it necessary to create the threads as detached? If you setqueue->min_threadsto zero all threads should exit as soon as they finish their work (which is better than exiting immediately).
This is historical because the code was largely inspired by glibc's implementation of posix-aio. It doesn't need to be detached and since Corentin wants to be able to join a worker, it makes sense to just avoid detaching and pay the overhead of making the threads joinable.
Regards,
Anthony Liguori
Quoted 2 lines
Paolo
Re: [Qemu-devel] Re: [PATCH V3 1/3] qemu: Add qemu-wrappers for pthread_attr_t
Quoted 12 lines · Anthony Liguori
Quoted 10 lines · Paolo Bonzini
Quoted 2 lines · Gautham R Shenoy
Add qemu wrappers for
pthread_attr_thandling.The point of these wrappers AFAIU is not only to add
error_exit, but also to be portable to Windows in the future. Is it necessary to create the threads as detached? If you setqueue->min_threadsto zero all threads should exit as soon as they finish their work (which is better than exiting immediately).
Quoted 8 lines
This is historical because the code was largely inspired by glibc's implementation of posix-aio. It doesn't need to be detached and since Corentin wants to be able to join a worker, it makes sense to just avoid detaching and pay the overhead of making the threads joinable.
Regards,
Anthony Liguori
Actually, I want to know if the queue is empty and if no job are currently being processed: all worker are idle or stopped. I don't really need pthread_join() for that, since worker can be idle (we don't want to always start and stop the thread :) ).
Signature · 2 lines
Corentin Chary http://xf.iksaif.net
Re: [Qemu-devel] Re: [PATCH V3 1/3] qemu: Add qemu-wrappers for pthread_attr_t
Quoted 8 lines · Corentin Chary
Quoted 7 lines
Quoted 2 lines
The point of these wrappers AFAIU is not only to add
error_exit, but also to be portable to Windows in the future.This is historical because the code was largely inspired by glibc's implementation of posix-aio. It doesn't need to be detached and since Corentin wants to be able to join a worker, it makes sense to just avoid detaching and pay the overhead of making the threads joinable.
Quoted 4 lines
Actually, I want to know if the queue is empty and if no job are currently being processed: all worker are idle or stopped. I don't really need
pthread_join()for that, since worker can be idle (we don't want to always start and stop the thread :) ).
Then it's also fine to have all qemu_threads detached (like in my patch to create all qemu_threads with blocked signals). I just want to avoid implementing pthreads one day for qemu-threads-win32.c.
Paolo
Re: [Qemu-devel] Re: [PATCH V3 1/3] qemu: Add qemu-wrappers for pthread_attr_t
In reply to Corentin CharyQuoted 13 lines · Corentin Chary
Quoted 12 lines · Anthony Liguori
Quoted 10 lines · Paolo Bonzini
Quoted 2 lines · Gautham R Shenoy
Add qemu wrappers for
pthread_attr_thandling.The point of these wrappers AFAIU is not only to add
error_exit, but also to be portable to Windows in the future. Is it necessary to create the threads as detached? If you setqueue->min_threadsto zero all threads should exit as soon as they finish their work (which is better than exiting immediately).
Quoted 9 lines
Quoted 8 lines
This is historical because the code was largely inspired by glibc's implementation of posix-aio. It doesn't need to be detached and since Corentin wants to be able to join a worker, it makes sense to just avoid detaching and pay the overhead of making the threads joinable.
Regards,
Anthony Liguori
Sorry for the late response. I had been away for the last few days.
Quoted 2 lines
Actually, I want to know if the queue is empty and if no job are currently being processed: all worker are idle or stopped.
In other words, you just want to know if the already submitted jobs have gotten over or not so that you could probably destroy the queue.
The subsystem using the queueing infrastructure is responsible for providing the guarantee that no new job would be submitted while/after making a call query the idleness of the queue.
If this understanding is correct, we might want to have a qemu_async_barrier() implementation which waits for all the threads finish processing the queued jobs, and destroy themselves.
Quoted 2 lines
I don't really need
pthread_join()for that, since worker can be idle (we don't want to always start and stop the thread :) ).
Signature · 2 lines
Thanks and Regards gautham
PATCH v3 2/3 qemu: Generic asynchronous threading framework to offload tasks
In reply to Gautham R ShenoyFrom: Aneesh Kumar K.V <[email protected]>
This patch creates a generic asynchronous-task-offloading infrastructure. It's extracted out of the threading framework that is being used by paio.
The reason for extracting out this generic infrastructure of the posix-aio-compat.c is so that other subsystems, such as virtio-9p could make use of it for offloading tasks that could block.
[[email protected]: work_item_pool, async_work_init, async_work_release, async_cancel_work]
--- Makefile.objs | 3 + async-work.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ async-work.h | 85 ++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletions(-) create mode 100644 async-work.c create mode 100644 async-work.h
Makefile.objs +2 -1
@@ -9,6 +9,8 @@ qobject-obj-y += qerror.o block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o block-obj-y += nbd.o block.o aio.o aes.o osdep.o qemu-config.o +block-obj-y += qemu-thread.o +block-obj-y += async-work.o block-obj-$(CONFIG_POSIX) += posix-aio-compat.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
@@ -108,7 +110,6 @@ common-obj-y += iov.o common-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o common-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o common-obj-$(CONFIG_COCOA) += cocoa.o -common-obj-$(CONFIG_IOTHREAD) += qemu-thread.o common-obj-y += notify.o event_notifier.o common-obj-y += qemu-timer.o
async-work.c new file +136 -0
136 lines not on this page · raw message
async-work.h new file +85 -0
85 lines not on this page · raw message
[Qemu-devel] Re: [PATCH V3 2/3] qemu: Generic asynchronous threading framework to offload tasks
Quoted 90 lines · Gautham R Shenoy
From: Aneesh Kumar K.V <[email protected]>
This patch creates a generic asynchronous-task-offloading infrastructure. It's extracted out of the threading framework that is being used by paio.
The reason for extracting out this generic infrastructure of the
posix-aio-compat.cis so that other subsystems, such as virtio-9p could make use of it for offloading tasks that could block.[[email protected]:
work_item_pool,async_work_init,async_work_release,async_cancel_work]Signed-off-by Aneesh Kumar K.V <[email protected]>Signed-off-by Gautham R Shenoy <[email protected]>---
Makefile.objs | 3 +
async-work.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++async-work.h| 85 ++++++++++++++++++++++++++++++++++++3 files changed, 223 insertions(+), 1 deletions(-)create mode 100644
async-work.c
create mode 100644async-work.hMakefile.objs +0
-0@@ -9,6 +9,8 @@ qobject-obj-y += qerror.oblock-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o block-obj-y += nbd.o block.o aio.o aes.o osdep.o qemu-config.o
Makefile.objs +2
-0+block-obj-y += qemu-thread.o +block-obj-y += async-work.oblock-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.oMakefile.objs +0
-0@@ -108,7 +110,6 @@ common-obj-y += iov.ocommon-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o common-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o common-obj-$(CONFIG_COCOA) += cocoa.o -common-obj-$(CONFIG_IOTHREAD) += qemu-thread.o common-obj-y += notify.o event_notifier.o common-obj-y += qemu-timer.o
async-work.c new file +41
-0@@ -0,0 +1,136 @@ +/* +* Async work support +* +* Copyright IBM, Corp. 2010 +* +* Authors: +* Aneesh Kumar K.V <[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +* +*/ +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include "async-work.h" +#include "osdep.h" + +static void async_abort (int err,const char *what) +{ + fprintf(stderr, "%s failed: %s\n", what, strerror(err)); + abort(); +} + +static void *async_worker_thread (void *data) +{ + struct async_queue *queue = data; + + while (1) { + struct work_item *work; + int ret = 0; + qemu_mutex_lock(&(queue->lock)); + + while (QTAILQ_EMPTY(&(queue->request_list)) && + (ret != ETIMEDOUT)) { + ret = qemu_cond_timedwait(&(queue->cond), + &(queue->lock), 10*100000); + } +
async-work.c line 51
Quoted 10 lines
async-work.c new file +10
-0+ if (QTAILQ_EMPTY(&(queue->request_list))) + goto check_exit; + + work = QTAILQ_FIRST(&(queue->request_list)); + QTAILQ_REMOVE(&(queue->request_list), work, node); + queue->idle_threads--; + qemu_mutex_unlock(&(queue->lock)); + + /* execute the work function */ + work->func(work);
Here the queue is empty, but there is a job running. In the VNC server I need to be able to "join" jobs (before resize, deconnection, etc..). What do you thing about adding something like qemu_async_join(queue, work) (if work is null, join all job) ?
Quoted 39 lines
async-work.c new file +39
-0+ async_work_release(queue, work); + + qemu_mutex_lock(&(queue->lock)); + queue->idle_threads++; + +check_exit: + if ((queue->idle_threads > 0) && + (queue->cur_threads > queue->min_threads)) { + /* we retain minimum number of threads */ + break; + } + qemu_mutex_unlock(&(queue->lock)); + } + + queue->idle_threads--; + queue->cur_threads--; + qemu_mutex_unlock(&(queue->lock)); + + return NULL; +} + +static void spawn_async_thread (struct async_queue *queue) +{ + QemuThreadAttr attr; + QemuThread thread; + sigset_t set, oldset; + + queue->cur_threads++; + queue->idle_threads++; + + qemu_thread_attr_init(&attr); + + /* create a detached thread so that we don't need to wait on it */ + qemu_thread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + /* block all signals */ + if (sigfillset(&set)) { + async_abort(errno, "sigfillset"); + }
async-work.c line 100
Quoted 10 lines
async-work.c new file +10
-0+ + if (sigprocmask(SIG_SETMASK, &set, &oldset)) { + async_abort(errno, "sigprocmask"); + } + + qemu_thread_create_attr(&thread, &attr, async_worker_thread, queue); + + if (sigprocmask(SIG_SETMASK, &oldset, NULL)) { + async_abort(errno, "sigprocmask restore"); + }
Using PTHREAD_CREATE_DETACHED and signal stuff here doesn't looks really portable. Can't we abstract that into qemu-thread (then, we just need port qemu-thread to windows) ?
Quoted 129 lines
async-work.c new file +36
-0+} + +void qemu_async_submit (struct async_queue *queue,struct work_item *work) +{ + qemu_mutex_lock(&(queue->lock)); + if (queue->idle_threads == 0 && queue->cur_threads < queue->max_threads) { + spawn_async_thread(queue); + } + QTAILQ_INSERT_TAIL(&(queue->request_list), work, node); + qemu_mutex_unlock(&(queue->lock)); + qemu_cond_signal(&(queue->cond)); +} + +int qemu_async_cancel_work (struct async_queue *queue,struct work_item *work) +{ + struct work_item *ret_work; + int found = 0; + + qemu_mutex_lock(&(queue->lock)); + QTAILQ_FOREACH(ret_work, &(queue->request_list), node) { + if (ret_work == work) { + QTAILQ_REMOVE(&(queue->request_list), ret_work, node); + found = 1; + break; + } + } + qemu_mutex_unlock(&(queue->lock)); + + if (found) { + async_work_release(queue, work); + return 0; + } + + return 1; +} +async-work.h new file +85
-0@@ -0,0 +1,85 @@ +/* +* Async work support +* +* Copyright IBM, Corp. 2010 +* +* Authors: +* Aneesh Kumar K.V <[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +* +*/ +#ifndef QEMU_ASYNC_WORK_H +#define QEMU_ASYNC_WORK_H + +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-thread.h" + +struct async_queue +{ + QemuMutex lock; + QemuCond cond; + int max_threads; + int min_threads; + int cur_threads; + int idle_threads; + QTAILQ_HEAD(, work_item) request_list; + QTAILQ_HEAD(, work_item) work_item_pool; +}; + +struct work_item +{ + QTAILQ_ENTRY(work_item) node; + void (*func)(struct work_item *work); + void *private; +}; + +static inline void async_queue_init (struct async_queue *queue, + int max_threads, int min_threads) +{ + queue->cur_threads = 0; + queue->idle_threads = 0; + queue->max_threads = max_threads; + queue->min_threads = min_threads; + QTAILQ_INIT(&(queue->request_list)); + QTAILQ_INIT(&(queue->work_item_pool)); + qemu_mutex_init(&(queue->lock)); + qemu_cond_init(&(queue->cond)); +} + +static inline struct work_item *async_work_init (struct async_queue *queue, + void (*func)(struct work_item *), + void *data) +{ + struct work_item *work; + qemu_mutex_lock(&(queue->lock)); + if (QTAILQ_EMPTY(&(queue->work_item_pool))) { + work = qemu_mallocz(sizeof(*work)); + } else { + work = QTAILQ_FIRST(&(queue->work_item_pool)); + QTAILQ_REMOVE(&(queue->work_item_pool), work, node); + } + + work->func = func; + work->private = data; + qemu_mutex_unlock(&(queue->lock)); + + return work; +} + +static inline void async_work_release (struct async_queue *queue, + struct work_item *work) +{ + qemu_mutex_lock(&(queue->lock)); + QTAILQ_INSERT_TAIL(&(queue->work_item_pool), work, node); + qemu_mutex_unlock(&(queue->lock)); +} + +extern void qemu_async_submit (struct async_queue *queue, + struct work_item *work); + +extern int qemu_async_cancel_work (struct async_queue *queue, + struct work_item *work); +#endif
Signature · 2 lines
Corentin Chary http://xf.iksaif.net
[Qemu-devel] Re: [PATCH V3 2/3] qemu: Generic asynchronous threading framework to offload tasks
Quoted 9 lines · Corentin Chary
Quoted 9 lines
+ if (
sigprocmask(SIG_SETMASK,&set,&oldset)) {
+async_abort(errno, "sigprocmask");
+ }
+
+qemu_thread_create_attr(&thread,&attr, async_worker_thread, queue);
+
+ if (sigprocmask(SIG_SETMASK,&oldset, NULL)) {
+async_abort(errno, "sigprocmask restore");
+ }
I wonder if qemu_thread_create shouldn't block all signals by default. Then the cpu and iothreads can unblock whatever they want.
I'll send a patch shortly.
In any case, please use pthread_sigmask instead of sigprocmask.
Paolo
Re: [Qemu-devel] [PATCH V3 2/3] qemu: Generic asynchronous threading framework to offload tasks
In reply to Gautham R ShenoyQuoted 50 lines · Gautham R Shenoy
From: Aneesh Kumar K.V<[email protected]>
This patch creates a generic asynchronous-task-offloading infrastructure. It's extracted out of the threading framework that is being used by paio.
The reason for extracting out this generic infrastructure of the
posix-aio-compat.cis so that other subsystems, such as virtio-9p could make use of it for offloading tasks that could block.[[email protected]:
work_item_pool,async_work_init,async_work_release,async_cancel_work]Signed-off-by Aneesh Kumar K.V<[email protected]>Signed-off-by Gautham R Shenoy<[email protected]>--- Makefile.objs | 3 + async-work.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ async-work.h | 85 ++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletions(-)create mode 100644
async-work.c
create mode 100644async-work.hMakefile.objs +2
-1@@ -9,6 +9,8 @@ qobject-obj-y += qerror.o block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o block-obj-y += nbd.o block.o aio.o aes.o osdep.o qemu-config.o +block-obj-y += qemu-thread.o +block-obj-y += async-work.o block-obj-$(CONFIG_POSIX) += posix-aio-compat.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o@@ -108,7 +110,6 @@ common-obj-y += iov.o common-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o common-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o common-obj-$(CONFIG_COCOA) += cocoa.o -common-obj-$(CONFIG_IOTHREAD) += qemu-thread.o common-obj-y += notify.o event_notifier.o common-obj-y += qemu-timer.oasync-work.c new file +0
-0
async-work.c line 10
Quoted 10 lines
async-work.c new file +10
-0@@ -0,0 +1,136 @@ +/* +* Async work support +* +* Copyright IBM, Corp. 2010 +* +* Authors: +* Aneesh Kumar K.V<[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory.
Please preserve the original copyright of the copied code.
async-work.c line 16
Quoted 7 lines
async-work.c new file +6
-0+*/ +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h>
qemu-common.h should have all of these. Generally, you should avoid including system headers because qemu headers take care of portability.
Quoted 153 lines
async-work.c new file +119
-0+#include "async-work.h" +#include "osdep.h" + +static void async_abort (int err,const char *what) +{ +fprintf (stderr,"%s failed: %s\n" , what,strerror (err)); +abort (); +} + +static void *async_worker_thread (void *data) +{ +struct async_queue *queue = data; + +while (1 ) { +struct work_item *work; +int ret =0 ; +qemu_mutex_lock (&(queue->lock)); + +while (QTAILQ_EMPTY (&(queue->request_list))&& + (ret !=ETIMEDOUT )) { + ret =qemu_cond_timedwait (&(queue->cond), + &(queue->lock),10 *100000 ); + } + +if (QTAILQ_EMPTY (&(queue->request_list))) + goto check_exit; + + work =QTAILQ_FIRST (&(queue->request_list)); +QTAILQ_REMOVE (&(queue->request_list), work, node); + queue->idle_threads--; +qemu_mutex_unlock (&(queue->lock)); + +/* execute the work function */ + work->func (work); +async_work_release (queue, work); + +qemu_mutex_lock (&(queue->lock)); + queue->idle_threads++; + +check_exit: +if ((queue->idle_threads>0 )&& + (queue->cur_threads> queue->min_threads)) { +/* we retain minimum number of threads */ +break ; + } +qemu_mutex_unlock (&(queue->lock)); + } + + queue->idle_threads--; + queue->cur_threads--; +qemu_mutex_unlock (&(queue->lock)); + +return NULL ; +} + +static void spawn_async_thread (struct async_queue *queue) +{ +QemuThreadAttr attr; +QemuThread thread; +sigset_t set, oldset; + + queue->cur_threads++; + queue->idle_threads++; + +qemu_thread_attr_init (&attr); + +/* create a detached thread so that we don't need to wait on it */ +qemu_thread_attr_setdetachstate (&attr,PTHREAD_CREATE_DETACHED ); + +/* block all signals */ +if (sigfillset (&set)) { +async_abort (errno,"sigfillset" ); + } + +if (sigprocmask (SIG_SETMASK ,&set,&oldset)) { +async_abort (errno,"sigprocmask" ); + } + +qemu_thread_create_attr (&thread,&attr, async_worker_thread, queue); + +if (sigprocmask (SIG_SETMASK ,&oldset,NULL )) { +async_abort (errno,"sigprocmask restore" ); + } +} + +void qemu_async_submit (struct async_queue *queue,struct work_item *work) +{ +qemu_mutex_lock (&(queue->lock)); +if (queue->idle_threads ==0 && queue->cur_threads< queue->max_threads) { +spawn_async_thread (queue); + } +QTAILQ_INSERT_TAIL (&(queue->request_list), work, node); +qemu_mutex_unlock (&(queue->lock)); +qemu_cond_signal (&(queue->cond)); +} + +int qemu_async_cancel_work (struct async_queue *queue,struct work_item *work) +{ +struct work_item *ret_work; +int found =0 ; + +qemu_mutex_lock (&(queue->lock)); + QTAILQ_FOREACH(ret_work,&(queue->request_list), node) { +if (ret_work == work) { +QTAILQ_REMOVE (&(queue->request_list), ret_work, node); + found =1 ; +break ; + } + } +qemu_mutex_unlock (&(queue->lock)); + +if (found) { +async_work_release (queue, work); +return 0 ; + } + +return 1 ; +} +async-work.h new file +27
-0@@ -0,0 +1,85 @@ +/* +* Async work support +* +* Copyright IBM, Corp. 2010 +* +* Authors: +* Aneesh Kumar K.V<[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +* +*/ +#ifndef QEMU_ASYNC_WORK_H +#define QEMU_ASYNC_WORK_H + +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-thread.h" + +struct async_queue +{ + QemuMutex lock; + QemuCond cond; + int max_threads; + int min_threads; + int cur_threads; + int idle_threads;
async-work.h line 37
Quoted 10 lines
async-work.h new file +10
-0+ QTAILQ_HEAD(, work_item) request_list; + QTAILQ_HEAD(, work_item) work_item_pool; +}; + +struct work_item +{ +QTAILQ_ENTRY (work_item) node; +void (*func)(struct work_item *work); +void *private; +};
Structs are not named in accordance to CODING_STYLE.
async-work.h line 50
Quoted 13 lines
async-work.h new file +12
-0+static inline void async_queue_init (struct async_queue *queue, +int max_threads,int min_threads) +{ + queue->cur_threads =0 ; + queue->idle_threads =0 ; + queue->max_threads = max_threads; + queue->min_threads = min_threads; +QTAILQ_INIT (&(queue->request_list)); +QTAILQ_INIT (&(queue->work_item_pool)); +qemu_mutex_init (&(queue->lock)); +qemu_cond_init (&(queue->cond)); +}
I'd prefer there be a single queue that everything used verses multiple queues. Otherwise, we'll end up having per device queues and my concern is that we'll end up with thousands and thousands of threads with no central place to tune the maximum thread number.
async-work.h line 54
Quoted 4 lines
async-work.h new file +3
-0+static inline struct work_item *async_work_init (struct async_queue *queue, +void (*func)(struct work_item *), +void *data)
I'd suggest actually using a Notifier as the worker or at least something that looks exactly like it. There's no need to pass a void * because more often than not, a caller just wants to pass a state structure anyway and they can embed the Notifier within the structure. IOW:
async_work_submit(queue, &s->worker);
Then in the callback:
DeviceState *s = container_of(worker, DeviceState, worker);
I don't think the name makes the most sense either. I think something like:
threadlet_submit()
Would work best. It would be good for there to be a big comment warning that the routine does not run with the qemu_mutex and therefore cannot make use of any qemu functions without very special consideration.
There shouldn't need to be an explicit init vs. submit function either.
Regards,
Anthony Liguori
Re: [Qemu-devel] [PATCH V3 2/3] qemu: Generic asynchronous threading framework to offload tasks
Quoted 257 lines · Anthony Liguori
Quoted 54 lines · Gautham R Shenoy
From: Aneesh Kumar K.V<[email protected]>
This patch creates a generic asynchronous-task-offloading infrastructure. It's
extracted out of the threading framework that is being used by paio.The reason for extracting out this generic infrastructure of the
posix-aio-compat.cis so that other subsystems, such as virtio-9p could make use
of it for offloading tasks that could block.[[email protected]:
work_item_pool,async_work_init,async_work_release,async_cancel_work]Signed-off-by Aneesh Kumar K.V<[email protected]>Signed-off-by Gautham R Shenoy<[email protected]>---
Makefile.objs | 3 +
async-work.c | 136
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
async-work.h | 85 ++++++++++++++++++++++++++++++++++++3 files changed, 223 insertions(+), 1 deletions(-)create mode 100644
async-work.c
create mode 100644async-work.hMakefile.objs +0
-0@@ -9,6 +9,8 @@ qobject-obj-y += qerror.oblock-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o block-obj-y += nbd.o block.o aio.o aes.o osdep.o qemu-config.o
Makefile.objs +2
-0+block-obj-y += qemu-thread.o +block-obj-y += async-work.oblock-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.oMakefile.objs +0
-0@@ -108,7 +110,6 @@ common-obj-y += iov.ocommon-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o common-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o common-obj-$(CONFIG_COCOA) += cocoa.o -common-obj-$(CONFIG_IOTHREAD) += qemu-thread.o common-obj-y += notify.o event_notifier.o common-obj-y += qemu-timer.o
async-work.c new file +0
-0async-work.c line 10
Quoted 10 lines
async-work.c new file +10
-0@@ -0,0 +1,136 @@ +/* +* Async work support +* +* Copyright IBM, Corp. 2010 +* +* Authors: +* Aneesh Kumar K.V<[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. Please preserve the original copyright of the copied code.
async-work.c line 16
Quoted 7 lines
async-work.c new file +6
-0+*/ +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h>
qemu-common.hshould have all of these. Generally, you should avoid including system headers because qemu headers take care of portability.Quoted 155 lines
async-work.c new file +89
-0+#include "async-work.h" +#include "osdep.h" + +static void async_abort (int err,const char *what) +{ + fprintf(stderr, "%s failed: %s\n", what, strerror(err)); + abort(); +} + +static void *async_worker_thread (void *data) +{ + struct async_queue *queue = data; + + while (1) { + struct work_item *work; + int ret = 0; + qemu_mutex_lock(&(queue->lock)); + + while (QTAILQ_EMPTY(&(queue->request_list))&& + (ret != ETIMEDOUT)) { + ret = qemu_cond_timedwait(&(queue->cond), + &(queue->lock), 10*100000); + } + + if (QTAILQ_EMPTY(&(queue->request_list))) + goto check_exit; + + work = QTAILQ_FIRST(&(queue->request_list)); + QTAILQ_REMOVE(&(queue->request_list), work, node); + queue->idle_threads--; + qemu_mutex_unlock(&(queue->lock)); + + /* execute the work function */ + work->func(work); + async_work_release(queue, work); + + qemu_mutex_lock(&(queue->lock)); + queue->idle_threads++; + +check_exit: + if ((queue->idle_threads> 0)&& + (queue->cur_threads> queue->min_threads)) { + /* we retain minimum number of threads */ + break; + } + qemu_mutex_unlock(&(queue->lock)); + } + + queue->idle_threads--; + queue->cur_threads--; + qemu_mutex_unlock(&(queue->lock)); + + return NULL; +} + +static void spawn_async_thread (struct async_queue *queue) +{ + QemuThreadAttr attr; + QemuThread thread; + sigset_t set, oldset; + + queue->cur_threads++; + queue->idle_threads++; + + qemu_thread_attr_init(&attr); + + /* create a detached thread so that we don't need to wait on it */ + qemu_thread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + /* block all signals */ + if (sigfillset(&set)) { + async_abort(errno, "sigfillset"); + } + + if (sigprocmask(SIG_SETMASK,&set,&oldset)) { + async_abort(errno, "sigprocmask"); + } + + qemu_thread_create_attr(&thread,&attr, async_worker_thread, queue); + + if (sigprocmask(SIG_SETMASK,&oldset, NULL)) { + async_abort(errno, "sigprocmask restore"); + } +} + +void qemu_async_submit(struct async_queue *queue, struct work_item *work) +{ + qemu_mutex_lock(&(queue->lock)); + if (queue->idle_threads == 0&& queue->cur_threads<queue->max_threads) {
+ spawn_async_thread(queue);
+ }
+ QTAILQ_INSERT_TAIL(&(queue->request_list), work, node);
+ qemu_mutex_unlock(&(queue->lock));
+ qemu_cond_signal(&(queue->cond));async-work.c new file +3
-0+} + +int qemu_async_cancel_work(struct async_queue *queue, struct work_item*work)
async-work.c new file +22
-0+{ + struct work_item *ret_work; + int found = 0; + + qemu_mutex_lock(&(queue->lock)); + QTAILQ_FOREACH(ret_work,&(queue->request_list), node) { + if (ret_work == work) { + QTAILQ_REMOVE(&(queue->request_list), ret_work, node); + found = 1; + break; + } + } + qemu_mutex_unlock(&(queue->lock)); + + if (found) { + async_work_release(queue, work); + return 0; + } + + return 1; +} +async-work.h new file +27
-0@@ -0,0 +1,85 @@ +/* +* Async work support +* +* Copyright IBM, Corp. 2010 +* +* Authors: +* Aneesh Kumar K.V<[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +* +*/ +#ifndef QEMU_ASYNC_WORK_H +#define QEMU_ASYNC_WORK_H + +#include "qemu-queue.h" +#include "qemu-common.h" +#include "qemu-thread.h" + +struct async_queue +{ + QemuMutex lock; + QemuCond cond; + int max_threads; + int min_threads; + int cur_threads; + int idle_threads;async-work.h line 37
Quoted 10 lines
async-work.h new file +10
-0+ QTAILQ_HEAD(, work_item) request_list; + QTAILQ_HEAD(, work_item) work_item_pool; +}; + +struct work_item +{ + QTAILQ_ENTRY(work_item) node; + void (*func)(struct work_item *work); + void *private; +};Structs are not named in accordance to
CODING_STYLE.Quoted 10 lines
async-work.h new file +10
-0+static inline void async_queue_init (struct async_queue *queue, + int max_threads, int min_threads) +{ + queue->cur_threads = 0; + queue->idle_threads = 0; + queue->max_threads = max_threads; + queue->min_threads = min_threads; + QTAILQ_INIT(&(queue->request_list)); + QTAILQ_INIT(&(queue->work_item_pool)); + qemu_mutex_init(&(queue->lock));
Quoted 8 lines
async-work.h line 49
Quoted 3 lines
async-work.h new file +2
-0+ qemu_cond_init(&(queue->cond)); +}I'd prefer there be a single queue that everything used verses multiple queues. Otherwise, we'll end up having per device queues and my concern is that we'll end up with thousands and thousands of threads with no central place to tune the maximum thread number.
If there a single queue, we'll need something to control how job are processed. For example,
in the VNC server, they must be processed in order (in fact, in order per VNC client, but I don't see how we could do that).
Quoted 32 lines
Quoted 5 lines
async-work.h new file +1
-0+static inline struct work_item *async_work_init(struct async_queue*queue,
+ void (*func)(structwork_item*),
+ void *data)I'd suggest actually using a Notifier as the worker or at least something that looks exactly like it. There's no need to pass a void * because more often than not, a caller just wants to pass a state structure anyway and they can embed the Notifier within the structure. IOW:
async_work_submit(queue, &s->worker);Then in the callback:
DeviceState *s =
container_of(worker, DeviceState, worker);I don't think the name makes the most sense either. I think something like:
threadlet_submit()Would work best. It would be good for there to be a big comment warning that the routine does not run with the
qemu_mutexand therefore cannot make use of any qemu functions without very special consideration.There shouldn't need to be an explicit init vs. submit function either.
Regards,
Anthony Liguori
Signature · 2 lines
Corentin Chary http://xf.iksaif.net
Re: [Qemu-devel] [PATCH V3 2/3] qemu: Generic asynchronous threading framework to offload tasks
In reply to Anthony LiguoriQuoted 8 lines · Anthony Liguori
Quoted 8 lines
async-work.c new file +5
-0@@ -0,0 +1,136 @@ +/* +* Async work support +* +* Copyright IBM, Corp. 2010 +*
Quoted 8 lines
async-work.c line 10
Quoted 6 lines
async-work.c new file +5
-0+* Authors: +* Aneesh Kumar K.V<[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. Please preserve the original copyright of the copied code.
Will update the comment containing the Copyright.
Quoted 11 lines
async-work.c line 17
Quoted 9 lines
async-work.c new file +7
-0+ +struct work_item +{ +QTAILQ_ENTRY (work_item) node; +void (*func)(struct work_item *work); +void *private; +};Structs are not named in accordance to
CODING_STYLE.
Will fix this.
Quoted 11 lines
Quoted 11 lines
async-work.c new file +10
-0+static inline void async_queue_init (struct async_queue *queue, +int max_threads,int min_threads) +{ + queue->cur_threads =0 ; + queue->idle_threads =0 ; + queue->max_threads = max_threads; + queue->min_threads = min_threads; +QTAILQ_INIT (&(queue->request_list)); +QTAILQ_INIT (&(queue->work_item_pool)); +qemu_mutex_init (&(queue->lock));
Quoted 8 lines
async-work.c line 29
Quoted 3 lines
async-work.c new file +2
-0+qemu_cond_init (&(queue->cond)); +}I'd prefer there be a single queue that everything used verses multiple queues. Otherwise, we'll end up having per device queues and my concern is that we'll end up with thousands and thousands of threads with no central place to tune the maximum thread number.
Aah! So, the original idea was to have a single queue, but since we were making it generic, we thought that the subsystems might like the flexibility of having their own queue.
I suppose we are not looking to differentiate between the worker threads belonging to different subsystems in terms of their relative importance/priorities, right ?
Quoted 13 lines
async-work.c line 32
Quoted 5 lines
async-work.c new file +3
-0+static inline struct work_item *async_work_init (struct async_queue *queue, +void (*func)(struct work_item *), +void *data)I'd suggest actually using a Notifier as the worker or at least something that looks exactly like it. There's no need to pass a void * because more often than not, a caller just wants to pass a state structure anyway and they can embed the Notifier within the structure. IOW:
async_work_submit(queue, &s->worker);
Quoted 7 lines
Then in the callback:
DeviceState *s =
container_of(worker, DeviceState, worker);I don't think the name makes the most sense either. I think something like:
threadlet_submit()
Makes sense. Will implement this.
Quoted 7 lines
Would work best. It would be good for there to be a big comment warning that the routine does not run with the
qemu_mutexand therefore cannot make use of any qemu functions without very special consideration.There shouldn't need to be an explicit init vs. submit function either.
Ok, will address these comments.
Quoted 4 lines
Regards,
Anthony Liguori
Signature · 2 lines
Thanks and Regards gautham
PATCH v3 3/3 qemu: Convert AIO code to use the generic threading infrastructure.
In reply to Gautham R ShenoyThis patch makes the paio subsystem use the generic work offloading infrastructure, there by decoupling asynchronous threading framework portion out of posix-aio-compat.c
The patch has been tested with fstress.
--- posix-aio-compat.c | 155 ++++++++++------------------------------------------ 1 files changed, 29 insertions(+), 126 deletions(-)
posix-aio-compat.c +29 -126
@@ -28,6 +28,7 @@#include "block_int.h" #include "block/raw-posix-aio.h" +#include "async-work.h" struct qemu_paiocb {
@@ -50,6 +51,7 @@ struct qemu_paiocb {struct qemu_paiocb *next;int async_context_id; +struct work_item *work; }; typedef struct PosixAioState {
@@ -57,15 +59,8 @@ typedef struct PosixAioState {struct qemu_paiocb *first_aio; } PosixAioState; - -static pthread_mutex_t lock =PTHREAD_MUTEX_INITIALIZER ; -static pthread_cond_t cond =PTHREAD_COND_INITIALIZER ; -static pthread_t thread_id; -static pthread_attr_t attr;static int max_threads =64 ; -static int cur_threads =0 ; -static int idle_threads =0 ; -static QTAILQ_HEAD(, qemu_paiocb) request_list; +static struct async_queue aio_request_list;#ifdef CONFIG_PREADV static int preadv_present =1 ;
@@ -84,39 +79,6 @@ static void die(const char *what)die2 (errno, what); } -static void mutex_lock (pthread_mutex_t *mutex) -{ -int ret =pthread_mutex_lock (mutex); -if (ret)die2 (ret,"pthread_mutex_lock" ); -} - -static void mutex_unlock (pthread_mutex_t *mutex) -{ -int ret =pthread_mutex_unlock (mutex); -if (ret)die2 (ret,"pthread_mutex_unlock" ); -} - -static int cond_timedwait (pthread_cond_t *cond,pthread_mutex_t *mutex, -struct timespec *ts) -{ -int ret =pthread_cond_timedwait (cond, mutex, ts); -if (ret && ret !=ETIMEDOUT )die2 (ret,"pthread_cond_timedwait" ); -return ret; -} - -static void cond_signal (pthread_cond_t *cond) -{ -int ret =pthread_cond_signal (cond); -if (ret)die2 (ret,"pthread_cond_signal" ); -} - -static void thread_create (pthread_t *thread,pthread_attr_t *attr, -void *(*start_routine)(void *),void *arg) -{ -int ret =pthread_create (thread, attr, start_routine, arg); -if (ret)die2 (ret,"pthread_create" ); -} -static ssize_t handle_aiocb_ioctl (struct qemu_paiocb *aiocb) {int ret;
@@ -300,47 +262,27 @@ static ssize_t handle_aiocb_rw(struct qemu_paiocb *aiocb)return nbytes; } -static void *aio_thread (void *unused) +static void aio_thread (struct work_item *work) { -pid_t pid; - pid =getpid (); - -while (1 ) { -struct qemu_paiocb *aiocb; -ssize_t ret =0 ; -qemu_timeval tv; -struct timespec ts; - -qemu_gettimeofday (&tv); - ts.tv_sec = tv.tv_sec +10 ; - ts.tv_nsec =0 ; - -mutex_lock (&lock); +pid_t pid; -while (QTAILQ_EMPTY (&request_list) && - !(ret ==ETIMEDOUT )) { - ret =cond_timedwait (&cond, &lock, &ts); - } +struct qemu_paiocb *aiocb = (struct qemu_paiocb *) work->private; +ssize_t ret =0 ; -if (QTAILQ_EMPTY (&request_list)) -break ; + pid =getpid (); - aiocb =QTAILQ_FIRST (&request_list); -QTAILQ_REMOVE (&request_list, aiocb, node); - aiocb->active =1 ; - idle_threads--; -mutex_unlock (&lock); + aiocb->active =1 ; -switch (aiocb->aio_type &QEMU_AIO_TYPE_MASK ) { -case QEMU_AIO_READ : -case QEMU_AIO_WRITE : +switch (aiocb->aio_type &QEMU_AIO_TYPE_MASK ) { +case QEMU_AIO_READ : +case QEMU_AIO_WRITE : ret =handle_aiocb_rw (aiocb);break ; -case QEMU_AIO_FLUSH : - ret =handle_aiocb_flush (aiocb); -break ; -case QEMU_AIO_IOCTL : +case QEMU_AIO_FLUSH : + ret =handle_aiocb_flush (aiocb); +break ; +case QEMU_AIO_IOCTL : ret =handle_aiocb_ioctl (aiocb);break ;default :
@@ -349,57 +291,28 @@ static void *aio_thread(void *unused)break ; } -mutex_lock (&lock); - aiocb->ret = ret; - idle_threads++; -mutex_unlock (&lock); - -if (kill (pid, aiocb->ev_signo))die ("kill failed" ); - } - - idle_threads--; - cur_threads--; -mutex_unlock (&lock); + aiocb->ret = ret; -return NULL ; -} - -static void spawn_thread (void ) -{ -sigset_t set, oldset; - - cur_threads++; - idle_threads++; - -/* block all signals */ -if (sigfillset (&set))die ("sigfillset" ); -if (sigprocmask (SIG_SETMASK , &set, &oldset))die ("sigprocmask" ); - -thread_create (&thread_id, &attr, aio_thread,NULL ); - -if (sigprocmask (SIG_SETMASK , &oldset,NULL ))die ("sigprocmask restore" ); +if (kill (pid, aiocb->ev_signo))die ("kill failed" ); }static void qemu_paio_submit (struct qemu_paiocb *aiocb) { +struct work_item *work; + aiocb->ret = -EINPROGRESS ; aiocb->active =0 ; -mutex_lock (&lock); -if (idle_threads ==0 && cur_threads < max_threads) -spawn_thread (); -QTAILQ_INSERT_TAIL (&request_list, aiocb, node); -mutex_unlock (&lock); -cond_signal (&cond); + + work =async_work_init (&aio_request_list, aio_thread, aiocb); + aiocb->work = work; +qemu_async_submit (&aio_request_list, work); }static ssize_t qemu_paio_return (struct qemu_paiocb *aiocb) {ssize_t ret; -mutex_lock (&lock); ret = aiocb->ret; -mutex_unlock (&lock); -return ret; }
@@ -535,14 +448,14 @@ static void paio_cancel(BlockDriverAIOCB *blockacb)struct qemu_paiocb *acb = (struct qemu_paiocb *)blockacb;int active =0 ; -mutex_lock (&lock);if (!acb->active) { -QTAILQ_REMOVE (&request_list, acb, node); - acb->ret = -ECANCELED ; +if (!qemu_async_cancel_work (&aio_request_list, acb->work)) + acb->ret = -ECANCELED ; +else + active =1 ; }else if (acb->ret == -EINPROGRESS ) { active =1 ; } -mutex_unlock (&lock);if (active) {/* fail safe: if the aio could not be canceled, we wait for
@@ -615,7 +528,6 @@ int paio_init(void)struct sigaction act;PosixAioState *s;int fds[2 ]; -int ret;if (posix_aio_state)return 0 ;
@@ -642,16 +554,7 @@ int paio_init(void)qemu_aio_set_fd_handler (s->rfd, posix_aio_read,NULL , posix_aio_flush, posix_aio_process_queue, s); - ret =pthread_attr_init (&attr); -if (ret) -die2 (ret,"pthread_attr_init" ); - - ret =pthread_attr_setdetachstate (&attr,PTHREAD_CREATE_DETACHED ); -if (ret) -die2 (ret,"pthread_attr_setdetachstate" ); - -QTAILQ_INIT (&request_list); - posix_aio_state = s; +async_queue_init (&aio_request_list, max_threads, max_threads);return 0 ; }