mirror of https://gitlab.com/qemu-project/qemu
dataplane: replace internal thread with IOThread
Today virtio-blk dataplane uses a 1:1 device-per-thread model. Now that IOThreads have been introduced we can generalize this to N:M devices per threads. This patch drops thread code from dataplane in favor of running inside an IOThread AioContext. As a bonus we solve the case where a guest keeps submitting I/O requests while dataplane is trying to stop. Previously the dataplane thread would continue to process requests until the request gave it a break. Now we can shut down in bounded time thanks to aio_context_acquire/release. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
6e4a876b43
commit
48ff269272
|
@ -23,6 +23,7 @@
|
||||||
#include "virtio-blk.h"
|
#include "virtio-blk.h"
|
||||||
#include "block/aio.h"
|
#include "block/aio.h"
|
||||||
#include "hw/virtio/virtio-bus.h"
|
#include "hw/virtio/virtio-bus.h"
|
||||||
|
#include "monitor/monitor.h" /* for object_add() */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SEG_MAX = 126, /* maximum number of I/O segments */
|
SEG_MAX = 126, /* maximum number of I/O segments */
|
||||||
|
@ -44,8 +45,6 @@ struct VirtIOBlockDataPlane {
|
||||||
bool started;
|
bool started;
|
||||||
bool starting;
|
bool starting;
|
||||||
bool stopping;
|
bool stopping;
|
||||||
QEMUBH *start_bh;
|
|
||||||
QemuThread thread;
|
|
||||||
|
|
||||||
VirtIOBlkConf *blk;
|
VirtIOBlkConf *blk;
|
||||||
int fd; /* image file descriptor */
|
int fd; /* image file descriptor */
|
||||||
|
@ -59,12 +58,14 @@ struct VirtIOBlockDataPlane {
|
||||||
* (because you don't own the file descriptor or handle; you just
|
* (because you don't own the file descriptor or handle; you just
|
||||||
* use it).
|
* use it).
|
||||||
*/
|
*/
|
||||||
|
IOThread *iothread;
|
||||||
|
bool internal_iothread;
|
||||||
AioContext *ctx;
|
AioContext *ctx;
|
||||||
EventNotifier io_notifier; /* Linux AIO completion */
|
EventNotifier io_notifier; /* Linux AIO completion */
|
||||||
EventNotifier host_notifier; /* doorbell */
|
EventNotifier host_notifier; /* doorbell */
|
||||||
|
|
||||||
IOQueue ioqueue; /* Linux AIO queue (should really be per
|
IOQueue ioqueue; /* Linux AIO queue (should really be per
|
||||||
dataplane thread) */
|
IOThread) */
|
||||||
VirtIOBlockRequest requests[REQ_MAX]; /* pool of requests, managed by the
|
VirtIOBlockRequest requests[REQ_MAX]; /* pool of requests, managed by the
|
||||||
queue */
|
queue */
|
||||||
|
|
||||||
|
@ -342,26 +343,7 @@ static void handle_io(EventNotifier *e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *data_plane_thread(void *opaque)
|
/* Context: QEMU global mutex held */
|
||||||
{
|
|
||||||
VirtIOBlockDataPlane *s = opaque;
|
|
||||||
|
|
||||||
while (!s->stopping || s->num_reqs > 0) {
|
|
||||||
aio_poll(s->ctx, true);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void start_data_plane_bh(void *opaque)
|
|
||||||
{
|
|
||||||
VirtIOBlockDataPlane *s = opaque;
|
|
||||||
|
|
||||||
qemu_bh_delete(s->start_bh);
|
|
||||||
s->start_bh = NULL;
|
|
||||||
qemu_thread_create(&s->thread, "data_plane", data_plane_thread,
|
|
||||||
s, QEMU_THREAD_JOINABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
|
void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
|
||||||
VirtIOBlockDataPlane **dataplane,
|
VirtIOBlockDataPlane **dataplane,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
|
@ -408,12 +390,33 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
|
||||||
s->fd = fd;
|
s->fd = fd;
|
||||||
s->blk = blk;
|
s->blk = blk;
|
||||||
|
|
||||||
|
if (blk->iothread) {
|
||||||
|
s->internal_iothread = false;
|
||||||
|
s->iothread = blk->iothread;
|
||||||
|
object_ref(OBJECT(s->iothread));
|
||||||
|
} else {
|
||||||
|
/* Create per-device IOThread if none specified */
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
s->internal_iothread = true;
|
||||||
|
object_add(TYPE_IOTHREAD, vdev->name, NULL, NULL, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
g_free(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s->iothread = iothread_find(vdev->name);
|
||||||
|
assert(s->iothread);
|
||||||
|
}
|
||||||
|
s->ctx = iothread_get_aio_context(s->iothread);
|
||||||
|
|
||||||
/* Prevent block operations that conflict with data plane thread */
|
/* Prevent block operations that conflict with data plane thread */
|
||||||
bdrv_set_in_use(blk->conf.bs, 1);
|
bdrv_set_in_use(blk->conf.bs, 1);
|
||||||
|
|
||||||
*dataplane = s;
|
*dataplane = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Context: QEMU global mutex held */
|
||||||
void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
|
void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
|
||||||
{
|
{
|
||||||
if (!s) {
|
if (!s) {
|
||||||
|
@ -422,9 +425,14 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
|
||||||
|
|
||||||
virtio_blk_data_plane_stop(s);
|
virtio_blk_data_plane_stop(s);
|
||||||
bdrv_set_in_use(s->blk->conf.bs, 0);
|
bdrv_set_in_use(s->blk->conf.bs, 0);
|
||||||
|
object_unref(OBJECT(s->iothread));
|
||||||
|
if (s->internal_iothread) {
|
||||||
|
object_unparent(OBJECT(s->iothread));
|
||||||
|
}
|
||||||
g_free(s);
|
g_free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Context: QEMU global mutex held */
|
||||||
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
||||||
{
|
{
|
||||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
|
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
|
||||||
|
@ -448,8 +456,6 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->ctx = aio_context_new();
|
|
||||||
|
|
||||||
/* Set up guest notifier (irq) */
|
/* Set up guest notifier (irq) */
|
||||||
if (k->set_guest_notifiers(qbus->parent, 1, true) != 0) {
|
if (k->set_guest_notifiers(qbus->parent, 1, true) != 0) {
|
||||||
fprintf(stderr, "virtio-blk failed to set guest notifier, "
|
fprintf(stderr, "virtio-blk failed to set guest notifier, "
|
||||||
|
@ -464,7 +470,6 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
s->host_notifier = *virtio_queue_get_host_notifier(vq);
|
s->host_notifier = *virtio_queue_get_host_notifier(vq);
|
||||||
aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify);
|
|
||||||
|
|
||||||
/* Set up ioqueue */
|
/* Set up ioqueue */
|
||||||
ioq_init(&s->ioqueue, s->fd, REQ_MAX);
|
ioq_init(&s->ioqueue, s->fd, REQ_MAX);
|
||||||
|
@ -472,7 +477,6 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
||||||
ioq_put_iocb(&s->ioqueue, &s->requests[i].iocb);
|
ioq_put_iocb(&s->ioqueue, &s->requests[i].iocb);
|
||||||
}
|
}
|
||||||
s->io_notifier = *ioq_get_notifier(&s->ioqueue);
|
s->io_notifier = *ioq_get_notifier(&s->ioqueue);
|
||||||
aio_set_event_notifier(s->ctx, &s->io_notifier, handle_io);
|
|
||||||
|
|
||||||
s->starting = false;
|
s->starting = false;
|
||||||
s->started = true;
|
s->started = true;
|
||||||
|
@ -481,11 +485,14 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
||||||
/* Kick right away to begin processing requests already in vring */
|
/* Kick right away to begin processing requests already in vring */
|
||||||
event_notifier_set(virtio_queue_get_host_notifier(vq));
|
event_notifier_set(virtio_queue_get_host_notifier(vq));
|
||||||
|
|
||||||
/* Spawn thread in BH so it inherits iothread cpusets */
|
/* Get this show started by hooking up our callbacks */
|
||||||
s->start_bh = qemu_bh_new(start_data_plane_bh, s);
|
aio_context_acquire(s->ctx);
|
||||||
qemu_bh_schedule(s->start_bh);
|
aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify);
|
||||||
|
aio_set_event_notifier(s->ctx, &s->io_notifier, handle_io);
|
||||||
|
aio_context_release(s->ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Context: QEMU global mutex held */
|
||||||
void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
|
void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
|
||||||
{
|
{
|
||||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
|
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
|
||||||
|
@ -496,27 +503,32 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
|
||||||
s->stopping = true;
|
s->stopping = true;
|
||||||
trace_virtio_blk_data_plane_stop(s);
|
trace_virtio_blk_data_plane_stop(s);
|
||||||
|
|
||||||
/* Stop thread or cancel pending thread creation BH */
|
aio_context_acquire(s->ctx);
|
||||||
if (s->start_bh) {
|
|
||||||
qemu_bh_delete(s->start_bh);
|
/* Stop notifications for new requests from guest */
|
||||||
s->start_bh = NULL;
|
aio_set_event_notifier(s->ctx, &s->host_notifier, NULL);
|
||||||
} else {
|
|
||||||
aio_notify(s->ctx);
|
/* Complete pending requests */
|
||||||
qemu_thread_join(&s->thread);
|
while (s->num_reqs > 0) {
|
||||||
|
aio_poll(s->ctx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stop ioq callbacks (there are no pending requests left) */
|
||||||
aio_set_event_notifier(s->ctx, &s->io_notifier, NULL);
|
aio_set_event_notifier(s->ctx, &s->io_notifier, NULL);
|
||||||
|
|
||||||
|
aio_context_release(s->ctx);
|
||||||
|
|
||||||
|
/* Sync vring state back to virtqueue so that non-dataplane request
|
||||||
|
* processing can continue when we disable the host notifier below.
|
||||||
|
*/
|
||||||
|
vring_teardown(&s->vring, s->vdev, 0);
|
||||||
|
|
||||||
ioq_cleanup(&s->ioqueue);
|
ioq_cleanup(&s->ioqueue);
|
||||||
|
|
||||||
aio_set_event_notifier(s->ctx, &s->host_notifier, NULL);
|
|
||||||
k->set_host_notifier(qbus->parent, 0, false);
|
k->set_host_notifier(qbus->parent, 0, false);
|
||||||
|
|
||||||
aio_context_unref(s->ctx);
|
|
||||||
|
|
||||||
/* Clean up guest notifier (irq) */
|
/* Clean up guest notifier (irq) */
|
||||||
k->set_guest_notifiers(qbus->parent, 1, false);
|
k->set_guest_notifiers(qbus->parent, 1, false);
|
||||||
|
|
||||||
vring_teardown(&s->vring, s->vdev, 0);
|
|
||||||
s->started = false;
|
s->started = false;
|
||||||
s->stopping = false;
|
s->stopping = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "hw/virtio/virtio.h"
|
#include "hw/virtio/virtio.h"
|
||||||
#include "hw/block/block.h"
|
#include "hw/block/block.h"
|
||||||
|
#include "sysemu/iothread.h"
|
||||||
|
|
||||||
#define TYPE_VIRTIO_BLK "virtio-blk-device"
|
#define TYPE_VIRTIO_BLK "virtio-blk-device"
|
||||||
#define VIRTIO_BLK(obj) \
|
#define VIRTIO_BLK(obj) \
|
||||||
|
@ -106,6 +107,7 @@ struct virtio_scsi_inhdr
|
||||||
struct VirtIOBlkConf
|
struct VirtIOBlkConf
|
||||||
{
|
{
|
||||||
BlockConf conf;
|
BlockConf conf;
|
||||||
|
IOThread *iothread;
|
||||||
char *serial;
|
char *serial;
|
||||||
uint32_t scsi;
|
uint32_t scsi;
|
||||||
uint32_t config_wce;
|
uint32_t config_wce;
|
||||||
|
@ -140,13 +142,15 @@ typedef struct VirtIOBlock {
|
||||||
DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \
|
DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \
|
||||||
DEFINE_PROP_STRING("serial", _state, _field.serial), \
|
DEFINE_PROP_STRING("serial", _state, _field.serial), \
|
||||||
DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true), \
|
DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true), \
|
||||||
DEFINE_PROP_BIT("scsi", _state, _field.scsi, 0, true)
|
DEFINE_PROP_BIT("scsi", _state, _field.scsi, 0, true), \
|
||||||
|
DEFINE_PROP_IOTHREAD("x-iothread", _state, _field.iothread)
|
||||||
#else
|
#else
|
||||||
#define DEFINE_VIRTIO_BLK_PROPERTIES(_state, _field) \
|
#define DEFINE_VIRTIO_BLK_PROPERTIES(_state, _field) \
|
||||||
DEFINE_BLOCK_PROPERTIES(_state, _field.conf), \
|
DEFINE_BLOCK_PROPERTIES(_state, _field.conf), \
|
||||||
DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \
|
DEFINE_BLOCK_CHS_PROPERTIES(_state, _field.conf), \
|
||||||
DEFINE_PROP_STRING("serial", _state, _field.serial), \
|
DEFINE_PROP_STRING("serial", _state, _field.serial), \
|
||||||
DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true)
|
DEFINE_PROP_BIT("config-wce", _state, _field.config_wce, 0, true), \
|
||||||
|
DEFINE_PROP_IOTHREAD("x-iothread", _state, _field.iothread)
|
||||||
#endif /* __linux__ */
|
#endif /* __linux__ */
|
||||||
|
|
||||||
void virtio_blk_set_conf(DeviceState *dev, VirtIOBlkConf *blk);
|
void virtio_blk_set_conf(DeviceState *dev, VirtIOBlkConf *blk);
|
||||||
|
|
Loading…
Reference in New Issue