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>
master
Stefan Hajnoczi 2014-03-03 11:30:08 +01:00
parent 6e4a876b43
commit 48ff269272
2 changed files with 60 additions and 44 deletions

View File

@ -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;
} }

View File

@ -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);