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----- 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>
This commit is contained in:
commit
178bacb66d
140
event-loop-base.c
Normal file
140
event-loop-base.c
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
@ -138,7 +138,7 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev)
|
|||||||
|
|
||||||
aio_context_acquire(s->ctx);
|
aio_context_acquire(s->ctx);
|
||||||
virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
|
virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
|
||||||
virtio_queue_aio_attach_host_notifier(vs->event_vq, s->ctx);
|
virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx);
|
||||||
|
|
||||||
for (i = 0; i < vs->conf.num_queues; i++) {
|
for (i = 0; i < vs->conf.num_queues; i++) {
|
||||||
virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
|
virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
|
||||||
|
@ -29,6 +29,43 @@
|
|||||||
#include "hw/virtio/virtio-access.h"
|
#include "hw/virtio/virtio-access.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
typedef struct VirtIOSCSIReq {
|
||||||
|
/*
|
||||||
|
* Note:
|
||||||
|
* - fields up to resp_iov are initialized by virtio_scsi_init_req;
|
||||||
|
* - fields starting at vring are zeroed by virtio_scsi_init_req.
|
||||||
|
*/
|
||||||
|
VirtQueueElement elem;
|
||||||
|
|
||||||
|
VirtIOSCSI *dev;
|
||||||
|
VirtQueue *vq;
|
||||||
|
QEMUSGList qsgl;
|
||||||
|
QEMUIOVector resp_iov;
|
||||||
|
|
||||||
|
union {
|
||||||
|
/* Used for two-stage request submission */
|
||||||
|
QTAILQ_ENTRY(VirtIOSCSIReq) next;
|
||||||
|
|
||||||
|
/* Used for cancellation of request during TMFs */
|
||||||
|
int remaining;
|
||||||
|
};
|
||||||
|
|
||||||
|
SCSIRequest *sreq;
|
||||||
|
size_t resp_size;
|
||||||
|
enum SCSIXferMode mode;
|
||||||
|
union {
|
||||||
|
VirtIOSCSICmdResp cmd;
|
||||||
|
VirtIOSCSICtrlTMFResp tmf;
|
||||||
|
VirtIOSCSICtrlANResp an;
|
||||||
|
VirtIOSCSIEvent event;
|
||||||
|
} resp;
|
||||||
|
union {
|
||||||
|
VirtIOSCSICmdReq cmd;
|
||||||
|
VirtIOSCSICtrlTMFReq tmf;
|
||||||
|
VirtIOSCSICtrlANReq an;
|
||||||
|
} req;
|
||||||
|
} VirtIOSCSIReq;
|
||||||
|
|
||||||
static inline int virtio_scsi_get_lun(uint8_t *lun)
|
static inline int virtio_scsi_get_lun(uint8_t *lun)
|
||||||
{
|
{
|
||||||
return ((lun[2] << 8) | lun[3]) & 0x3FFF;
|
return ((lun[2] << 8) | lun[3]) & 0x3FFF;
|
||||||
@ -45,7 +82,7 @@ static inline SCSIDevice *virtio_scsi_device_get(VirtIOSCSI *s, uint8_t *lun)
|
|||||||
return scsi_device_get(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
|
return scsi_device_get(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
|
||||||
}
|
}
|
||||||
|
|
||||||
void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req)
|
static void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req)
|
||||||
{
|
{
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||||
const size_t zero_skip =
|
const size_t zero_skip =
|
||||||
@ -58,7 +95,7 @@ void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req)
|
|||||||
memset((uint8_t *)req + zero_skip, 0, sizeof(*req) - zero_skip);
|
memset((uint8_t *)req + zero_skip, 0, sizeof(*req) - zero_skip);
|
||||||
}
|
}
|
||||||
|
|
||||||
void virtio_scsi_free_req(VirtIOSCSIReq *req)
|
static void virtio_scsi_free_req(VirtIOSCSIReq *req)
|
||||||
{
|
{
|
||||||
qemu_iovec_destroy(&req->resp_iov);
|
qemu_iovec_destroy(&req->resp_iov);
|
||||||
qemu_sglist_destroy(&req->qsgl);
|
qemu_sglist_destroy(&req->qsgl);
|
||||||
@ -460,28 +497,41 @@ static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq)
|
static void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq)
|
||||||
{
|
{
|
||||||
VirtIOSCSIReq *req;
|
VirtIOSCSIReq *req;
|
||||||
bool progress = false;
|
|
||||||
|
|
||||||
while ((req = virtio_scsi_pop_req(s, vq))) {
|
while ((req = virtio_scsi_pop_req(s, vq))) {
|
||||||
progress = true;
|
|
||||||
virtio_scsi_handle_ctrl_req(s, req);
|
virtio_scsi_handle_ctrl_req(s, req);
|
||||||
}
|
}
|
||||||
return progress;
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If dataplane is configured but not yet started, do so now and return true on
|
||||||
|
* success.
|
||||||
|
*
|
||||||
|
* Dataplane is started by the core virtio code but virtqueue handler functions
|
||||||
|
* can also be invoked when a guest kicks before DRIVER_OK, so this helper
|
||||||
|
* function helps us deal with manually starting ioeventfd in that case.
|
||||||
|
*/
|
||||||
|
static bool virtio_scsi_defer_to_dataplane(VirtIOSCSI *s)
|
||||||
|
{
|
||||||
|
if (!s->ctx || s->dataplane_started) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtio_device_start_ioeventfd(&s->parent_obj.parent_obj);
|
||||||
|
return !s->dataplane_fenced;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
{
|
{
|
||||||
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
||||||
|
|
||||||
if (s->ctx) {
|
if (virtio_scsi_defer_to_dataplane(s)) {
|
||||||
virtio_device_start_ioeventfd(vdev);
|
return;
|
||||||
if (!s->dataplane_fenced) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtio_scsi_acquire(s);
|
virtio_scsi_acquire(s);
|
||||||
virtio_scsi_handle_ctrl_vq(s, vq);
|
virtio_scsi_handle_ctrl_vq(s, vq);
|
||||||
virtio_scsi_release(s);
|
virtio_scsi_release(s);
|
||||||
@ -672,12 +722,11 @@ static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
|||||||
scsi_req_unref(sreq);
|
scsi_req_unref(sreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
|
static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
|
||||||
{
|
{
|
||||||
VirtIOSCSIReq *req, *next;
|
VirtIOSCSIReq *req, *next;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
bool suppress_notifications = virtio_queue_get_notification(vq);
|
bool suppress_notifications = virtio_queue_get_notification(vq);
|
||||||
bool progress = false;
|
|
||||||
|
|
||||||
QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
|
QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
|
||||||
|
|
||||||
@ -687,7 +736,6 @@ bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while ((req = virtio_scsi_pop_req(s, vq))) {
|
while ((req = virtio_scsi_pop_req(s, vq))) {
|
||||||
progress = true;
|
|
||||||
ret = virtio_scsi_handle_cmd_req_prepare(s, req);
|
ret = virtio_scsi_handle_cmd_req_prepare(s, req);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
QTAILQ_INSERT_TAIL(&reqs, req, next);
|
QTAILQ_INSERT_TAIL(&reqs, req, next);
|
||||||
@ -712,7 +760,6 @@ bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
|
|||||||
QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
|
QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
|
||||||
virtio_scsi_handle_cmd_req_submit(s, req);
|
virtio_scsi_handle_cmd_req_submit(s, req);
|
||||||
}
|
}
|
||||||
return progress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
|
static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
@ -720,12 +767,10 @@ static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)
|
|||||||
/* use non-QOM casts in the data path */
|
/* use non-QOM casts in the data path */
|
||||||
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
|
||||||
|
|
||||||
if (s->ctx && !s->dataplane_started) {
|
if (virtio_scsi_defer_to_dataplane(s)) {
|
||||||
virtio_device_start_ioeventfd(vdev);
|
return;
|
||||||
if (!s->dataplane_fenced) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtio_scsi_acquire(s);
|
virtio_scsi_acquire(s);
|
||||||
virtio_scsi_handle_cmd_vq(s, vq);
|
virtio_scsi_handle_cmd_vq(s, vq);
|
||||||
virtio_scsi_release(s);
|
virtio_scsi_release(s);
|
||||||
@ -793,8 +838,8 @@ static void virtio_scsi_reset(VirtIODevice *vdev)
|
|||||||
s->events_dropped = false;
|
s->events_dropped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||||
uint32_t event, uint32_t reason)
|
uint32_t event, uint32_t reason)
|
||||||
{
|
{
|
||||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||||
VirtIOSCSIReq *req;
|
VirtIOSCSIReq *req;
|
||||||
@ -842,25 +887,21 @@ void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
|||||||
virtio_scsi_complete_req(req);
|
virtio_scsi_complete_req(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq)
|
static void virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq)
|
||||||
{
|
{
|
||||||
if (s->events_dropped) {
|
if (s->events_dropped) {
|
||||||
virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
|
virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq)
|
static void virtio_scsi_handle_event(VirtIODevice *vdev, VirtQueue *vq)
|
||||||
{
|
{
|
||||||
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
||||||
|
|
||||||
if (s->ctx) {
|
if (virtio_scsi_defer_to_dataplane(s)) {
|
||||||
virtio_device_start_ioeventfd(vdev);
|
return;
|
||||||
if (!s->dataplane_fenced) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtio_scsi_acquire(s);
|
virtio_scsi_acquire(s);
|
||||||
virtio_scsi_handle_event_vq(s, vq);
|
virtio_scsi_handle_event_vq(s, vq);
|
||||||
virtio_scsi_release(s);
|
virtio_scsi_release(s);
|
||||||
|
@ -3534,6 +3534,19 @@ void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx)
|
|||||||
virtio_queue_host_notifier_aio_poll_end);
|
virtio_queue_host_notifier_aio_poll_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same as virtio_queue_aio_attach_host_notifier() but without polling. Use
|
||||||
|
* this for rx virtqueues and similar cases where the virtqueue handler
|
||||||
|
* function does not pop all elements. When the virtqueue is left non-empty
|
||||||
|
* polling consumes CPU cycles and should not be used.
|
||||||
|
*/
|
||||||
|
void virtio_queue_aio_attach_host_notifier_no_poll(VirtQueue *vq, AioContext *ctx)
|
||||||
|
{
|
||||||
|
aio_set_event_notifier(ctx, &vq->host_notifier, true,
|
||||||
|
virtio_queue_host_notifier_read,
|
||||||
|
NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx)
|
void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx)
|
||||||
{
|
{
|
||||||
aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL, NULL, NULL);
|
aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL, NULL, NULL);
|
||||||
|
@ -192,6 +192,8 @@ struct AioContext {
|
|||||||
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
|
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
|
||||||
QEMUBH *co_schedule_bh;
|
QEMUBH *co_schedule_bh;
|
||||||
|
|
||||||
|
int thread_pool_min;
|
||||||
|
int thread_pool_max;
|
||||||
/* Thread pool for performing work and receiving completion callbacks.
|
/* Thread pool for performing work and receiving completion callbacks.
|
||||||
* Has its own locking.
|
* Has its own locking.
|
||||||
*/
|
*/
|
||||||
@ -769,4 +771,12 @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
|
|||||||
void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
|
void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aio_context_set_thread_pool_params:
|
||||||
|
* @ctx: the aio context
|
||||||
|
* @min: min number of threads to have readily available in the thread pool
|
||||||
|
* @min: max number of threads the thread pool can contain
|
||||||
|
*/
|
||||||
|
void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
|
||||||
|
int64_t max, Error **errp);
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "block/block.h"
|
#include "block/block.h"
|
||||||
|
|
||||||
|
#define THREAD_POOL_MAX_THREADS_DEFAULT 64
|
||||||
|
|
||||||
typedef int ThreadPoolFunc(void *opaque);
|
typedef int ThreadPoolFunc(void *opaque);
|
||||||
|
|
||||||
typedef struct ThreadPool ThreadPool;
|
typedef struct ThreadPool ThreadPool;
|
||||||
@ -33,5 +35,6 @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
|
|||||||
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
|
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
|
||||||
ThreadPoolFunc *func, void *arg);
|
ThreadPoolFunc *func, void *arg);
|
||||||
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
|
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
|
||||||
|
void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -92,42 +92,6 @@ struct VirtIOSCSI {
|
|||||||
uint32_t host_features;
|
uint32_t host_features;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct VirtIOSCSIReq {
|
|
||||||
/* Note:
|
|
||||||
* - fields up to resp_iov are initialized by virtio_scsi_init_req;
|
|
||||||
* - fields starting at vring are zeroed by virtio_scsi_init_req.
|
|
||||||
* */
|
|
||||||
VirtQueueElement elem;
|
|
||||||
|
|
||||||
VirtIOSCSI *dev;
|
|
||||||
VirtQueue *vq;
|
|
||||||
QEMUSGList qsgl;
|
|
||||||
QEMUIOVector resp_iov;
|
|
||||||
|
|
||||||
union {
|
|
||||||
/* Used for two-stage request submission */
|
|
||||||
QTAILQ_ENTRY(VirtIOSCSIReq) next;
|
|
||||||
|
|
||||||
/* Used for cancellation of request during TMFs */
|
|
||||||
int remaining;
|
|
||||||
};
|
|
||||||
|
|
||||||
SCSIRequest *sreq;
|
|
||||||
size_t resp_size;
|
|
||||||
enum SCSIXferMode mode;
|
|
||||||
union {
|
|
||||||
VirtIOSCSICmdResp cmd;
|
|
||||||
VirtIOSCSICtrlTMFResp tmf;
|
|
||||||
VirtIOSCSICtrlANResp an;
|
|
||||||
VirtIOSCSIEvent event;
|
|
||||||
} resp;
|
|
||||||
union {
|
|
||||||
VirtIOSCSICmdReq cmd;
|
|
||||||
VirtIOSCSICtrlTMFReq tmf;
|
|
||||||
VirtIOSCSICtrlANReq an;
|
|
||||||
} req;
|
|
||||||
} VirtIOSCSIReq;
|
|
||||||
|
|
||||||
static inline void virtio_scsi_acquire(VirtIOSCSI *s)
|
static inline void virtio_scsi_acquire(VirtIOSCSI *s)
|
||||||
{
|
{
|
||||||
if (s->ctx) {
|
if (s->ctx) {
|
||||||
@ -149,13 +113,6 @@ void virtio_scsi_common_realize(DeviceState *dev,
|
|||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
void virtio_scsi_common_unrealize(DeviceState *dev);
|
void virtio_scsi_common_unrealize(DeviceState *dev);
|
||||||
bool virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq);
|
|
||||||
bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq);
|
|
||||||
bool virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq);
|
|
||||||
void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req);
|
|
||||||
void virtio_scsi_free_req(VirtIOSCSIReq *req);
|
|
||||||
void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
|
||||||
uint32_t event, uint32_t reason);
|
|
||||||
|
|
||||||
void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp);
|
void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp);
|
||||||
int virtio_scsi_dataplane_start(VirtIODevice *s);
|
int virtio_scsi_dataplane_start(VirtIODevice *s);
|
||||||
|
@ -317,6 +317,7 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
|
|||||||
void virtio_queue_set_host_notifier_enabled(VirtQueue *vq, bool enabled);
|
void virtio_queue_set_host_notifier_enabled(VirtQueue *vq, bool enabled);
|
||||||
void virtio_queue_host_notifier_read(EventNotifier *n);
|
void virtio_queue_host_notifier_read(EventNotifier *n);
|
||||||
void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx);
|
void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx);
|
||||||
|
void virtio_queue_aio_attach_host_notifier_no_poll(VirtQueue *vq, AioContext *ctx);
|
||||||
void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx);
|
void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx);
|
||||||
VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector);
|
VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector);
|
||||||
VirtQueue *virtio_vector_next_queue(VirtQueue *vq);
|
VirtQueue *virtio_vector_next_queue(VirtQueue *vq);
|
||||||
|
@ -26,9 +26,19 @@
|
|||||||
#define QEMU_MAIN_LOOP_H
|
#define QEMU_MAIN_LOOP_H
|
||||||
|
|
||||||
#include "block/aio.h"
|
#include "block/aio.h"
|
||||||
|
#include "qom/object.h"
|
||||||
|
#include "sysemu/event-loop-base.h"
|
||||||
|
|
||||||
#define SIG_IPI SIGUSR1
|
#define SIG_IPI SIGUSR1
|
||||||
|
|
||||||
|
#define TYPE_MAIN_LOOP "main-loop"
|
||||||
|
OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP)
|
||||||
|
|
||||||
|
struct MainLoop {
|
||||||
|
EventLoopBase parent_obj;
|
||||||
|
};
|
||||||
|
typedef struct MainLoop MainLoop;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qemu_init_main_loop: Set up the process so that it can run the main loop.
|
* qemu_init_main_loop: Set up the process so that it can run the main loop.
|
||||||
*
|
*
|
||||||
|
41
include/sysemu/event-loop-base.h
Normal file
41
include/sysemu/event-loop-base.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* QEMU event-loop backend
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 Red Hat Inc
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#ifndef QEMU_EVENT_LOOP_BASE_H
|
||||||
|
#define QEMU_EVENT_LOOP_BASE_H
|
||||||
|
|
||||||
|
#include "qom/object.h"
|
||||||
|
#include "block/aio.h"
|
||||||
|
#include "qemu/typedefs.h"
|
||||||
|
|
||||||
|
#define TYPE_EVENT_LOOP_BASE "event-loop-base"
|
||||||
|
OBJECT_DECLARE_TYPE(EventLoopBase, EventLoopBaseClass,
|
||||||
|
EVENT_LOOP_BASE)
|
||||||
|
|
||||||
|
struct EventLoopBaseClass {
|
||||||
|
ObjectClass parent_class;
|
||||||
|
|
||||||
|
void (*init)(EventLoopBase *base, Error **errp);
|
||||||
|
void (*update_params)(EventLoopBase *base, Error **errp);
|
||||||
|
bool (*can_be_deleted)(EventLoopBase *base);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EventLoopBase {
|
||||||
|
Object parent;
|
||||||
|
|
||||||
|
/* AioContext AIO engine parameters */
|
||||||
|
int64_t aio_max_batch;
|
||||||
|
|
||||||
|
/* AioContext thread pool parameters */
|
||||||
|
int64_t thread_pool_min;
|
||||||
|
int64_t thread_pool_max;
|
||||||
|
};
|
||||||
|
#endif
|
@ -17,11 +17,12 @@
|
|||||||
#include "block/aio.h"
|
#include "block/aio.h"
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
#include "sysemu/event-loop-base.h"
|
||||||
|
|
||||||
#define TYPE_IOTHREAD "iothread"
|
#define TYPE_IOTHREAD "iothread"
|
||||||
|
|
||||||
struct IOThread {
|
struct IOThread {
|
||||||
Object parent_obj;
|
EventLoopBase parent_obj;
|
||||||
|
|
||||||
QemuThread thread;
|
QemuThread thread;
|
||||||
AioContext *ctx;
|
AioContext *ctx;
|
||||||
@ -37,9 +38,6 @@ struct IOThread {
|
|||||||
int64_t poll_max_ns;
|
int64_t poll_max_ns;
|
||||||
int64_t poll_grow;
|
int64_t poll_grow;
|
||||||
int64_t poll_shrink;
|
int64_t poll_shrink;
|
||||||
|
|
||||||
/* AioContext AIO engine parameters */
|
|
||||||
int64_t aio_max_batch;
|
|
||||||
};
|
};
|
||||||
typedef struct IOThread IOThread;
|
typedef struct IOThread IOThread;
|
||||||
|
|
||||||
|
68
iothread.c
68
iothread.c
@ -17,6 +17,7 @@
|
|||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "block/aio.h"
|
#include "block/aio.h"
|
||||||
#include "block/block.h"
|
#include "block/block.h"
|
||||||
|
#include "sysemu/event-loop-base.h"
|
||||||
#include "sysemu/iothread.h"
|
#include "sysemu/iothread.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qapi/qapi-commands-misc.h"
|
#include "qapi/qapi-commands-misc.h"
|
||||||
@ -152,10 +153,15 @@ static void iothread_init_gcontext(IOThread *iothread)
|
|||||||
iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE);
|
iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
|
static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
|
||||||
{
|
{
|
||||||
|
IOThread *iothread = IOTHREAD(base);
|
||||||
ERRP_GUARD();
|
ERRP_GUARD();
|
||||||
|
|
||||||
|
if (!iothread->ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
aio_context_set_poll_params(iothread->ctx,
|
aio_context_set_poll_params(iothread->ctx,
|
||||||
iothread->poll_max_ns,
|
iothread->poll_max_ns,
|
||||||
iothread->poll_grow,
|
iothread->poll_grow,
|
||||||
@ -166,14 +172,18 @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
aio_context_set_aio_params(iothread->ctx,
|
aio_context_set_aio_params(iothread->ctx,
|
||||||
iothread->aio_max_batch,
|
iothread->parent_obj.aio_max_batch,
|
||||||
errp);
|
errp);
|
||||||
|
|
||||||
|
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;
|
Error *local_error = NULL;
|
||||||
IOThread *iothread = IOTHREAD(obj);
|
IOThread *iothread = IOTHREAD(base);
|
||||||
char *thread_name;
|
char *thread_name;
|
||||||
|
|
||||||
iothread->stopping = false;
|
iothread->stopping = false;
|
||||||
@ -189,7 +199,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
|
|||||||
*/
|
*/
|
||||||
iothread_init_gcontext(iothread);
|
iothread_init_gcontext(iothread);
|
||||||
|
|
||||||
iothread_set_aio_context_params(iothread, &local_error);
|
iothread_set_aio_context_params(base, &local_error);
|
||||||
if (local_error) {
|
if (local_error) {
|
||||||
error_propagate(errp, local_error);
|
error_propagate(errp, local_error);
|
||||||
aio_context_unref(iothread->ctx);
|
aio_context_unref(iothread->ctx);
|
||||||
@ -201,7 +211,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
|
|||||||
* to inherit.
|
* to inherit.
|
||||||
*/
|
*/
|
||||||
thread_name = g_strdup_printf("IO %s",
|
thread_name = g_strdup_printf("IO %s",
|
||||||
object_get_canonical_path_component(OBJECT(obj)));
|
object_get_canonical_path_component(OBJECT(base)));
|
||||||
qemu_thread_create(&iothread->thread, thread_name, iothread_run,
|
qemu_thread_create(&iothread->thread, thread_name, iothread_run,
|
||||||
iothread, QEMU_THREAD_JOINABLE);
|
iothread, QEMU_THREAD_JOINABLE);
|
||||||
g_free(thread_name);
|
g_free(thread_name);
|
||||||
@ -226,9 +236,6 @@ static IOThreadParamInfo poll_grow_info = {
|
|||||||
static IOThreadParamInfo poll_shrink_info = {
|
static IOThreadParamInfo poll_shrink_info = {
|
||||||
"poll-shrink", offsetof(IOThread, poll_shrink),
|
"poll-shrink", offsetof(IOThread, poll_shrink),
|
||||||
};
|
};
|
||||||
static IOThreadParamInfo aio_max_batch_info = {
|
|
||||||
"aio-max-batch", offsetof(IOThread, aio_max_batch),
|
|
||||||
};
|
|
||||||
|
|
||||||
static void iothread_get_param(Object *obj, Visitor *v,
|
static void iothread_get_param(Object *obj, Visitor *v,
|
||||||
const char *name, IOThreadParamInfo *info, Error **errp)
|
const char *name, IOThreadParamInfo *info, Error **errp)
|
||||||
@ -288,35 +295,12 @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iothread_get_aio_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_aio_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_aio_params(iothread->ctx,
|
|
||||||
iothread->aio_max_batch,
|
|
||||||
errp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void iothread_class_init(ObjectClass *klass, void *class_data)
|
static void iothread_class_init(ObjectClass *klass, void *class_data)
|
||||||
{
|
{
|
||||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
|
EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
|
||||||
ucc->complete = iothread_complete;
|
|
||||||
|
bc->init = iothread_init;
|
||||||
|
bc->update_params = iothread_set_aio_context_params;
|
||||||
|
|
||||||
object_class_property_add(klass, "poll-max-ns", "int",
|
object_class_property_add(klass, "poll-max-ns", "int",
|
||||||
iothread_get_poll_param,
|
iothread_get_poll_param,
|
||||||
@ -330,23 +314,15 @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
|
|||||||
iothread_get_poll_param,
|
iothread_get_poll_param,
|
||||||
iothread_set_poll_param,
|
iothread_set_poll_param,
|
||||||
NULL, &poll_shrink_info);
|
NULL, &poll_shrink_info);
|
||||||
object_class_property_add(klass, "aio-max-batch", "int",
|
|
||||||
iothread_get_aio_param,
|
|
||||||
iothread_set_aio_param,
|
|
||||||
NULL, &aio_max_batch_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo iothread_info = {
|
static const TypeInfo iothread_info = {
|
||||||
.name = TYPE_IOTHREAD,
|
.name = TYPE_IOTHREAD,
|
||||||
.parent = TYPE_OBJECT,
|
.parent = TYPE_EVENT_LOOP_BASE,
|
||||||
.class_init = iothread_class_init,
|
.class_init = iothread_class_init,
|
||||||
.instance_size = sizeof(IOThread),
|
.instance_size = sizeof(IOThread),
|
||||||
.instance_init = iothread_instance_init,
|
.instance_init = iothread_instance_init,
|
||||||
.instance_finalize = iothread_instance_finalize,
|
.instance_finalize = iothread_instance_finalize,
|
||||||
.interfaces = (InterfaceInfo[]) {
|
|
||||||
{TYPE_USER_CREATABLE},
|
|
||||||
{}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void iothread_register_types(void)
|
static void iothread_register_types(void)
|
||||||
@ -383,7 +359,7 @@ static int query_one_iothread(Object *object, void *opaque)
|
|||||||
info->poll_max_ns = iothread->poll_max_ns;
|
info->poll_max_ns = iothread->poll_max_ns;
|
||||||
info->poll_grow = iothread->poll_grow;
|
info->poll_grow = iothread->poll_grow;
|
||||||
info->poll_shrink = iothread->poll_shrink;
|
info->poll_shrink = iothread->poll_shrink;
|
||||||
info->aio_max_batch = iothread->aio_max_batch;
|
info->aio_max_batch = iothread->parent_obj.aio_max_batch;
|
||||||
|
|
||||||
QAPI_LIST_APPEND(*tail, info);
|
QAPI_LIST_APPEND(*tail, info);
|
||||||
return 0;
|
return 0;
|
||||||
|
26
meson.build
26
meson.build
@ -3025,6 +3025,7 @@ subdir('qom')
|
|||||||
subdir('authz')
|
subdir('authz')
|
||||||
subdir('crypto')
|
subdir('crypto')
|
||||||
subdir('ui')
|
subdir('ui')
|
||||||
|
subdir('hw')
|
||||||
|
|
||||||
|
|
||||||
if enable_modules
|
if enable_modules
|
||||||
@ -3032,6 +3033,18 @@ if enable_modules
|
|||||||
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
|
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
qom_ss = qom_ss.apply(config_host, strict: false)
|
||||||
|
libqom = static_library('qom', qom_ss.sources() + genh,
|
||||||
|
dependencies: [qom_ss.dependencies()],
|
||||||
|
name_suffix: 'fa')
|
||||||
|
qom = declare_dependency(link_whole: libqom)
|
||||||
|
|
||||||
|
event_loop_base = files('event-loop-base.c')
|
||||||
|
event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh,
|
||||||
|
build_by_default: true)
|
||||||
|
event_loop_base = declare_dependency(link_whole: event_loop_base,
|
||||||
|
dependencies: [qom])
|
||||||
|
|
||||||
stub_ss = stub_ss.apply(config_all, strict: false)
|
stub_ss = stub_ss.apply(config_all, strict: false)
|
||||||
|
|
||||||
util_ss.add_all(trace_ss)
|
util_ss.add_all(trace_ss)
|
||||||
@ -3040,7 +3053,8 @@ libqemuutil = static_library('qemuutil',
|
|||||||
sources: util_ss.sources() + stub_ss.sources() + genh,
|
sources: util_ss.sources() + stub_ss.sources() + genh,
|
||||||
dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman])
|
dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman])
|
||||||
qemuutil = declare_dependency(link_with: libqemuutil,
|
qemuutil = declare_dependency(link_with: libqemuutil,
|
||||||
sources: genh + version_res)
|
sources: genh + version_res,
|
||||||
|
dependencies: [event_loop_base])
|
||||||
|
|
||||||
if have_system or have_user
|
if have_system or have_user
|
||||||
decodetree = generator(find_program('scripts/decodetree.py'),
|
decodetree = generator(find_program('scripts/decodetree.py'),
|
||||||
@ -3118,7 +3132,6 @@ subdir('monitor')
|
|||||||
subdir('net')
|
subdir('net')
|
||||||
subdir('replay')
|
subdir('replay')
|
||||||
subdir('semihosting')
|
subdir('semihosting')
|
||||||
subdir('hw')
|
|
||||||
subdir('tcg')
|
subdir('tcg')
|
||||||
subdir('fpu')
|
subdir('fpu')
|
||||||
subdir('accel')
|
subdir('accel')
|
||||||
@ -3243,13 +3256,6 @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms',
|
|||||||
capture: true,
|
capture: true,
|
||||||
command: [undefsym, nm, '@INPUT@'])
|
command: [undefsym, nm, '@INPUT@'])
|
||||||
|
|
||||||
qom_ss = qom_ss.apply(config_host, strict: false)
|
|
||||||
libqom = static_library('qom', qom_ss.sources() + genh,
|
|
||||||
dependencies: [qom_ss.dependencies()],
|
|
||||||
name_suffix: 'fa')
|
|
||||||
|
|
||||||
qom = declare_dependency(link_whole: libqom)
|
|
||||||
|
|
||||||
authz_ss = authz_ss.apply(config_host, strict: false)
|
authz_ss = authz_ss.apply(config_host, strict: false)
|
||||||
libauthz = static_library('authz', authz_ss.sources() + genh,
|
libauthz = static_library('authz', authz_ss.sources() + genh,
|
||||||
dependencies: [authz_ss.dependencies()],
|
dependencies: [authz_ss.dependencies()],
|
||||||
@ -3302,7 +3308,7 @@ libblockdev = static_library('blockdev', blockdev_ss.sources() + genh,
|
|||||||
build_by_default: false)
|
build_by_default: false)
|
||||||
|
|
||||||
blockdev = declare_dependency(link_whole: [libblockdev],
|
blockdev = declare_dependency(link_whole: [libblockdev],
|
||||||
dependencies: [block])
|
dependencies: [block, event_loop_base])
|
||||||
|
|
||||||
qmp_ss = qmp_ss.apply(config_host, strict: false)
|
qmp_ss = qmp_ss.apply(config_host, strict: false)
|
||||||
libqmp = static_library('qmp', qmp_ss.sources() + genh,
|
libqmp = static_library('qmp', qmp_ss.sources() + genh,
|
||||||
|
@ -499,6 +499,28 @@
|
|||||||
'*repeat': 'bool',
|
'*repeat': 'bool',
|
||||||
'*grab-toggle': 'GrabToggleKeys' } }
|
'*grab-toggle': 'GrabToggleKeys' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @EventLoopBaseProperties:
|
||||||
|
#
|
||||||
|
# Common properties for event loops
|
||||||
|
#
|
||||||
|
# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
|
||||||
|
# 0 means that the engine will use its default.
|
||||||
|
# (default: 0)
|
||||||
|
#
|
||||||
|
# @thread-pool-min: minimum number of threads reserved in the thread pool
|
||||||
|
# (default:0)
|
||||||
|
#
|
||||||
|
# @thread-pool-max: maximum number of threads the thread pool can contain
|
||||||
|
# (default:64)
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
##
|
||||||
|
{ 'struct': 'EventLoopBaseProperties',
|
||||||
|
'data': { '*aio-max-batch': 'int',
|
||||||
|
'*thread-pool-min': 'int',
|
||||||
|
'*thread-pool-max': 'int' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @IothreadProperties:
|
# @IothreadProperties:
|
||||||
#
|
#
|
||||||
@ -516,17 +538,26 @@
|
|||||||
# algorithm detects it is spending too long polling without
|
# algorithm detects it is spending too long polling without
|
||||||
# encountering events. 0 selects a default behaviour (default: 0)
|
# encountering events. 0 selects a default behaviour (default: 0)
|
||||||
#
|
#
|
||||||
# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
|
# The @aio-max-batch option is available since 6.1.
|
||||||
# 0 means that the engine will use its default
|
|
||||||
# (default:0, since 6.1)
|
|
||||||
#
|
#
|
||||||
# Since: 2.0
|
# Since: 2.0
|
||||||
##
|
##
|
||||||
{ 'struct': 'IothreadProperties',
|
{ 'struct': 'IothreadProperties',
|
||||||
|
'base': 'EventLoopBaseProperties',
|
||||||
'data': { '*poll-max-ns': 'int',
|
'data': { '*poll-max-ns': 'int',
|
||||||
'*poll-grow': 'int',
|
'*poll-grow': 'int',
|
||||||
'*poll-shrink': 'int',
|
'*poll-shrink': 'int' } }
|
||||||
'*aio-max-batch': 'int' } }
|
|
||||||
|
##
|
||||||
|
# @MainLoopProperties:
|
||||||
|
#
|
||||||
|
# Properties for the main-loop object.
|
||||||
|
#
|
||||||
|
# Since: 7.1
|
||||||
|
##
|
||||||
|
{ 'struct': 'MainLoopProperties',
|
||||||
|
'base': 'EventLoopBaseProperties',
|
||||||
|
'data': {} }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @MemoryBackendProperties:
|
# @MemoryBackendProperties:
|
||||||
@ -818,6 +849,7 @@
|
|||||||
{ 'name': 'input-linux',
|
{ 'name': 'input-linux',
|
||||||
'if': 'CONFIG_LINUX' },
|
'if': 'CONFIG_LINUX' },
|
||||||
'iothread',
|
'iothread',
|
||||||
|
'main-loop',
|
||||||
{ 'name': 'memory-backend-epc',
|
{ 'name': 'memory-backend-epc',
|
||||||
'if': 'CONFIG_LINUX' },
|
'if': 'CONFIG_LINUX' },
|
||||||
'memory-backend-file',
|
'memory-backend-file',
|
||||||
@ -883,6 +915,7 @@
|
|||||||
'input-linux': { 'type': 'InputLinuxProperties',
|
'input-linux': { 'type': 'InputLinuxProperties',
|
||||||
'if': 'CONFIG_LINUX' },
|
'if': 'CONFIG_LINUX' },
|
||||||
'iothread': 'IothreadProperties',
|
'iothread': 'IothreadProperties',
|
||||||
|
'main-loop': 'MainLoopProperties',
|
||||||
'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties',
|
'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties',
|
||||||
'if': 'CONFIG_LINUX' },
|
'if': 'CONFIG_LINUX' },
|
||||||
'memory-backend-file': 'MemoryBackendFileProperties',
|
'memory-backend-file': 'MemoryBackendFileProperties',
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "block/block.h"
|
#include "block/block.h"
|
||||||
|
#include "block/thread-pool.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "qemu/rcu.h"
|
#include "qemu/rcu.h"
|
||||||
#include "qemu/rcu_queue.h"
|
#include "qemu/rcu_queue.h"
|
||||||
|
20
util/async.c
20
util/async.c
@ -563,6 +563,9 @@ AioContext *aio_context_new(Error **errp)
|
|||||||
|
|
||||||
ctx->aio_max_batch = 0;
|
ctx->aio_max_batch = 0;
|
||||||
|
|
||||||
|
ctx->thread_pool_min = 0;
|
||||||
|
ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
fail:
|
fail:
|
||||||
g_source_destroy(&ctx->source);
|
g_source_destroy(&ctx->source);
|
||||||
@ -696,3 +699,20 @@ void qemu_set_current_aio_context(AioContext *ctx)
|
|||||||
assert(!get_my_aiocontext());
|
assert(!get_my_aiocontext());
|
||||||
set_my_aiocontext(ctx);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -30,9 +30,11 @@
|
|||||||
#include "sysemu/replay.h"
|
#include "sysemu/replay.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "block/aio.h"
|
#include "block/aio.h"
|
||||||
|
#include "block/thread-pool.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
#include "qemu/compiler.h"
|
#include "qemu/compiler.h"
|
||||||
|
#include "qom/object.h"
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@ -184,6 +186,69 @@ int qemu_init_main_loop(Error **errp)
|
|||||||
return 0;
|
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;
|
static int max_priority;
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
|
@ -58,7 +58,6 @@ struct ThreadPool {
|
|||||||
QemuMutex lock;
|
QemuMutex lock;
|
||||||
QemuCond worker_stopped;
|
QemuCond worker_stopped;
|
||||||
QemuSemaphore sem;
|
QemuSemaphore sem;
|
||||||
int max_threads;
|
|
||||||
QEMUBH *new_thread_bh;
|
QEMUBH *new_thread_bh;
|
||||||
|
|
||||||
/* The following variables are only accessed from one AioContext. */
|
/* 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 new_threads; /* backlog of threads we need to create */
|
||||||
int pending_threads; /* threads created but not running yet */
|
int pending_threads; /* threads created but not running yet */
|
||||||
bool stopping;
|
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)
|
static void *worker_thread(void *opaque)
|
||||||
{
|
{
|
||||||
ThreadPool *pool = opaque;
|
ThreadPool *pool = opaque;
|
||||||
@ -91,8 +109,9 @@ static void *worker_thread(void *opaque)
|
|||||||
ret = qemu_sem_timedwait(&pool->sem, 10000);
|
ret = qemu_sem_timedwait(&pool->sem, 10000);
|
||||||
qemu_mutex_lock(&pool->lock);
|
qemu_mutex_lock(&pool->lock);
|
||||||
pool->idle_threads--;
|
pool->idle_threads--;
|
||||||
} while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list));
|
} while (back_to_sleep(pool, ret));
|
||||||
if (ret == -1 || pool->stopping) {
|
if (ret == -1 || pool->stopping ||
|
||||||
|
pool->cur_threads > pool->max_threads) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,6 +313,33 @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg)
|
|||||||
thread_pool_submit_aio(pool, func, arg, NULL, NULL);
|
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)
|
static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
|
||||||
{
|
{
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
@ -306,11 +352,12 @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
|
|||||||
qemu_mutex_init(&pool->lock);
|
qemu_mutex_init(&pool->lock);
|
||||||
qemu_cond_init(&pool->worker_stopped);
|
qemu_cond_init(&pool->worker_stopped);
|
||||||
qemu_sem_init(&pool->sem, 0);
|
qemu_sem_init(&pool->sem, 0);
|
||||||
pool->max_threads = 64;
|
|
||||||
pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool);
|
pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool);
|
||||||
|
|
||||||
QLIST_INIT(&pool->head);
|
QLIST_INIT(&pool->head);
|
||||||
QTAILQ_INIT(&pool->request_list);
|
QTAILQ_INIT(&pool->request_list);
|
||||||
|
|
||||||
|
thread_pool_update_params(pool, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool *thread_pool_new(AioContext *ctx)
|
ThreadPool *thread_pool_new(AioContext *ctx)
|
||||||
|
Loading…
Reference in New Issue
Block a user