[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH RFC 14/14] dataplane: virtio-blk: support mutlti vir
From: |
Ming Lei |
Subject: |
[Qemu-devel] [PATCH RFC 14/14] dataplane: virtio-blk: support mutlti virtqueue |
Date: |
Thu, 17 Jul 2014 00:31:21 +0800 |
This patch supports to handle host notify from multi
virt queues, but still process/submit io in the
one iothread.
Currently this patch brings up below improvement
with two virtqueues(against single virtqueue):
---------------------------------------------------
| VM in server host | VM in laptop host
---------------------------------------------------
JOBS=2 | +8% | -11%
---------------------------------------------------
JOBS=4 | +64% | +29%
---------------------------------------------------
The reason is that commit 580b6b2aa2(dataplane: use the QEMU
block layer for I/O) causes average submitting time for single
request doubled, so io thread performance get decreased.
Based on QEMU 2.0, only this single patch can achieve
very good improvement:
http://marc.info/?l=linux-api&m=140377573830230&w=2
So hope QEMU block layer can get optimized for linux aio,
or maybe a fast path is needed for linux aio.
Signed-off-by: Ming Lei <address@hidden>
---
hw/block/dataplane/virtio-blk.c | 209 ++++++++++++++++++++++++++++-----------
include/hw/virtio/virtio-blk.h | 1 +
2 files changed, 153 insertions(+), 57 deletions(-)
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 828fe99..bd66274 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -26,6 +26,11 @@
#define REQ_POOL_SZ 128
+typedef struct {
+ EventNotifier notifier;
+ VirtIOBlockDataPlane *s;
+} VirtIOBlockNotifier;
+
struct VirtIOBlockDataPlane {
bool started;
bool starting;
@@ -35,9 +40,10 @@ struct VirtIOBlockDataPlane {
VirtIOBlkConf *blk;
VirtIODevice *vdev;
- Vring vring; /* virtqueue vring */
- EventNotifier *guest_notifier; /* irq */
- QEMUBH *bh; /* bh for guest notification */
+ Vring *vring; /* virtqueue vring */
+ EventNotifier **guest_notifier; /* irq */
+ uint64_t pending_guest_notifier; /* pending guest notifer for vq */
+ QEMUBH *bh; /* bh for guest notification */
/* Note that these EventNotifiers are assigned by value. This is
* fine as long as you do not call event_notifier_cleanup on them
@@ -47,7 +53,9 @@ struct VirtIOBlockDataPlane {
IOThread *iothread;
IOThread internal_iothread_obj;
AioContext *ctx;
- EventNotifier host_notifier; /* doorbell */
+ VirtIOBlockNotifier *host_notifier; /* doorbell */
+ uint64_t pending_host_notifier; /* pending host notifer for vq */
+ QEMUBH *host_notifier_bh; /* for handle host notifier */
/* Operation blocker on BDS */
Error *blocker;
@@ -60,20 +68,26 @@ struct VirtIOBlockDataPlane {
};
/* Raise an interrupt to signal guest, if necessary */
-static void notify_guest(VirtIOBlockDataPlane *s)
+static void notify_guest(VirtIOBlockDataPlane *s, unsigned int qid)
{
- if (!vring_should_notify(s->vdev, &s->vring)) {
- return;
+ if (vring_should_notify(s->vdev, &s->vring[qid])) {
+ event_notifier_set(s->guest_notifier[qid]);
}
-
- event_notifier_set(s->guest_notifier);
}
static void notify_guest_bh(void *opaque)
{
VirtIOBlockDataPlane *s = opaque;
+ unsigned int qid;
+ uint64_t pending = s->pending_guest_notifier;
+
+ s->pending_guest_notifier = 0;
- notify_guest(s);
+ while ((qid = ffsl(pending))) {
+ qid--;
+ notify_guest(s, qid);
+ pending &= ~(1 << qid);
+ }
}
static void complete_request_vring(VirtIOBlockReq *req, unsigned char status)
@@ -81,7 +95,7 @@ static void complete_request_vring(VirtIOBlockReq *req,
unsigned char status)
VirtIOBlockDataPlane *s = req->dev->dataplane;
stb_p(&req->in->status, status);
- vring_push(&req->dev->dataplane->vring, &req->elem,
+ vring_push(&s->vring[req->qid], &req->elem,
req->qiov.size + sizeof(*req->in));
/* Suppress notification to guest by BH and its scheduled
@@ -90,17 +104,15 @@ static void complete_request_vring(VirtIOBlockReq *req,
unsigned char status)
* executed in dataplane aio context even after it is
* stopped, so needn't worry about notification loss with BH.
*/
+ assert(req->qid < 64);
+ s->pending_guest_notifier |= (1 << req->qid);
qemu_bh_schedule(s->bh);
}
-static void handle_notify(EventNotifier *e)
+static void process_vq_notify(VirtIOBlockDataPlane *s, unsigned short qid)
{
- VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane,
- host_notifier);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
- event_notifier_test_and_clear(&s->host_notifier);
- bdrv_io_plug(s->blk->conf.bs);
for (;;) {
MultiReqBuffer mrb = {
.num_writes = 0,
@@ -108,12 +120,13 @@ static void handle_notify(EventNotifier *e)
int ret;
/* Disable guest->host notifies to avoid unnecessary vmexits */
- vring_disable_notification(s->vdev, &s->vring);
+ vring_disable_notification(s->vdev, &s->vring[qid]);
for (;;) {
VirtIOBlockReq *req = virtio_blk_alloc_request(vblk);
- ret = vring_pop(s->vdev, &s->vring, &req->elem);
+ req->qid = qid;
+ ret = vring_pop(s->vdev, &s->vring[qid], &req->elem);
if (ret < 0) {
virtio_blk_free_request(req);
break; /* no more requests */
@@ -132,16 +145,48 @@ static void handle_notify(EventNotifier *e)
/* Re-enable guest->host notifies and stop processing the vring.
* But if the guest has snuck in more descriptors, keep processing.
*/
- if (vring_enable_notification(s->vdev, &s->vring)) {
+ if (vring_enable_notification(s->vdev, &s->vring[qid])) {
break;
}
} else { /* fatal error */
break;
}
}
+}
+
+static void process_notify(void *opaque)
+{
+ VirtIOBlockDataPlane *s = opaque;
+ unsigned int qid;
+ uint64_t pending = s->pending_host_notifier;
+
+ s->pending_host_notifier = 0;
+
+ bdrv_io_plug(s->blk->conf.bs);
+ while ((qid = ffsl(pending))) {
+ qid--;
+ process_vq_notify(s, qid);
+ pending &= ~(1 << qid);
+ }
bdrv_io_unplug(s->blk->conf.bs);
}
+/* TODO: handle requests from other vqs together */
+static void handle_notify(EventNotifier *e)
+{
+ VirtIOBlockNotifier *n = container_of(e, VirtIOBlockNotifier,
+ notifier);
+ VirtIOBlockDataPlane *s = n->s;
+ unsigned int qid = n - &s->host_notifier[0];
+
+ assert(qid < 64);
+
+ event_notifier_test_and_clear(e);
+
+ s->pending_host_notifier |= (1 << qid);
+ qemu_bh_schedule(s->host_notifier_bh);
+}
+
/* Context: QEMU global mutex held */
void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk,
VirtIOBlockDataPlane **dataplane,
@@ -197,6 +242,11 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev,
VirtIOBlkConf *blk,
s->ctx = iothread_get_aio_context(s->iothread);
s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
+ s->vring = g_new0(Vring, blk->num_queues);
+ s->guest_notifier = g_new(EventNotifier *, blk->num_queues);
+ s->host_notifier = g_new(VirtIOBlockNotifier, blk->num_queues);
+ s->host_notifier_bh = aio_bh_new(s->ctx, process_notify, s);
+
error_setg(&s->blocker, "block device is in use by data plane");
bdrv_op_block_all(blk->conf.bs, s->blocker);
@@ -217,16 +267,83 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane
*s)
error_free(s->blocker);
object_unref(OBJECT(s->iothread));
qemu_bh_delete(s->bh);
+ qemu_bh_delete(s->host_notifier_bh);
+ g_free(s->vring);
+ g_free(s->guest_notifier);
+ g_free(s->host_notifier);
g_free(s);
}
+static int pre_start_vq(VirtIOBlockDataPlane *s, BusState *qbus,
+ VirtioBusClass *k)
+{
+ int i;
+ int num = s->blk->num_queues;
+ VirtQueue *vq[num];
+
+ for (i = 0; i < num; i++) {
+ vq[i] = virtio_get_queue(s->vdev, i);
+ if (!vring_setup(&s->vring[i], s->vdev, i)) {
+ return -1;
+ }
+ }
+
+ /* Set up guest notifier (irq) */
+ if (k->set_guest_notifiers(qbus->parent, num, true) != 0) {
+ fprintf(stderr, "virtio-blk failed to set guest notifier, "
+ "ensure -enable-kvm is set\n");
+ exit(1);
+ }
+
+ for (i = 0; i < num; i++)
+ s->guest_notifier[i] = virtio_queue_get_guest_notifier(vq[i]);
+ s->pending_guest_notifier = 0;
+
+ /* Set up virtqueue notify */
+ for (i = 0; i < num; i++) {
+ if (k->set_host_notifier(qbus->parent, i, true) != 0) {
+ fprintf(stderr, "virtio-blk failed to set host notifier\n");
+ exit(1);
+ }
+ s->host_notifier[i].notifier = *virtio_queue_get_host_notifier(vq[i]);
+ s->host_notifier[i].s = s;
+ }
+ s->pending_host_notifier = 0;
+
+ return 0;
+}
+
+static void post_start_vq(VirtIOBlockDataPlane *s)
+{
+ int i;
+ int num = s->blk->num_queues;
+
+ for (i = 0; i < num; i++) {
+ VirtQueue *vq;
+ vq = virtio_get_queue(s->vdev, i);
+
+ /* Kick right away to begin processing requests already in vring */
+ event_notifier_set(virtio_queue_get_host_notifier(vq));
+ }
+
+ if (s->raw_format) {
+ qemu_aio_set_bypass_co(s->ctx, true);
+ }
+
+ /* Get this show started by hooking up our callbacks */
+ aio_context_acquire(s->ctx);
+ for (i = 0; i < num; i++)
+ aio_set_event_notifier(s->ctx, &s->host_notifier[i].notifier,
+ handle_notify);
+ aio_context_release(s->ctx);
+}
+
/* Context: QEMU global mutex held */
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
- VirtQueue *vq;
if (s->started) {
return;
@@ -238,51 +355,24 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
s->starting = true;
- vq = virtio_get_queue(s->vdev, 0);
- if (!vring_setup(&s->vring, s->vdev, 0)) {
- s->starting = false;
- return;
- }
-
vblk->obj_pool = &s->req_pool;
obj_pool_init(vblk->obj_pool, s->reqs, s->free_reqs,
sizeof(VirtIOBlockReq), REQ_POOL_SZ);
- /* Set up guest notifier (irq) */
- if (k->set_guest_notifiers(qbus->parent, 1, true) != 0) {
- fprintf(stderr, "virtio-blk failed to set guest notifier, "
- "ensure -enable-kvm is set\n");
- exit(1);
- }
- s->guest_notifier = virtio_queue_get_guest_notifier(vq);
-
- /* Set up virtqueue notify */
- if (k->set_host_notifier(qbus->parent, 0, true) != 0) {
- fprintf(stderr, "virtio-blk failed to set host notifier\n");
- exit(1);
- }
- s->host_notifier = *virtio_queue_get_host_notifier(vq);
-
s->saved_complete_request = vblk->complete_request;
vblk->complete_request = complete_request_vring;
+ if (pre_start_vq(s, qbus, k)) {
+ s->starting = false;
+ return;
+ }
+
s->starting = false;
s->started = true;
trace_virtio_blk_data_plane_start(s);
bdrv_set_aio_context(s->blk->conf.bs, s->ctx);
-
- /* Kick right away to begin processing requests already in vring */
- event_notifier_set(virtio_queue_get_host_notifier(vq));
-
- if (s->raw_format) {
- qemu_aio_set_bypass_co(s->ctx, true);
- }
-
- /* Get this show started by hooking up our callbacks */
- aio_context_acquire(s->ctx);
- aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify);
- aio_context_release(s->ctx);
+ post_start_vq(s);
}
/* Context: QEMU global mutex held */
@@ -291,6 +381,8 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
+ int i;
+ int num = s->blk->num_queues;
if (!s->started || s->stopping) {
return;
}
@@ -301,7 +393,8 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
aio_context_acquire(s->ctx);
/* Stop notifications for new requests from guest */
- aio_set_event_notifier(s->ctx, &s->host_notifier, NULL);
+ for (i = 0; i < num; i++)
+ aio_set_event_notifier(s->ctx, &s->host_notifier[i].notifier, NULL);
/* Drain and switch bs back to the QEMU main loop */
bdrv_set_aio_context(s->blk->conf.bs, qemu_get_aio_context());
@@ -316,12 +409,14 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
/* 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);
+ for (i = 0; i < num; i++)
+ vring_teardown(&s->vring[i], s->vdev, 0);
- k->set_host_notifier(qbus->parent, 0, false);
+ for (i = 0; i < num; i++)
+ k->set_host_notifier(qbus->parent, i, false);
/* Clean up guest notifier (irq) */
- k->set_guest_notifiers(qbus->parent, 1, false);
+ k->set_guest_notifiers(qbus->parent, num, false);
s->started = false;
s->stopping = false;
diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h
index 91489b0..e7795e4 100644
--- a/include/hw/virtio/virtio-blk.h
+++ b/include/hw/virtio/virtio-blk.h
@@ -164,6 +164,7 @@ typedef struct VirtIOBlockReq {
QEMUIOVector qiov;
struct VirtIOBlockReq *next;
BlockAcctCookie acct;
+ unsigned qid;
} VirtIOBlockReq;
VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s);
--
1.7.9.5
- [Qemu-devel] [PATCH RFC 04/14] Revert "raw-posix: drop raw_get_aio_fd() since it is no longer used", (continued)
- [Qemu-devel] [PATCH RFC 04/14] Revert "raw-posix: drop raw_get_aio_fd() since it is no longer used", Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 05/14] dataplane: enable selective bypassing coroutine, Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 06/14] qemu/obj_pool.h: introduce object allocation pool, Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 07/14] dataplane: use object pool to speed up allocation for virtio blk request, Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 08/14] linux-aio: fix submit aio as a batch, Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 09/14] linux-aio: increase max event to 256, Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 10/14] linux-aio: remove 'node' from 'struct qemu_laiocb', Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 11/14] hw/virtio-pci: introduce num_queues property, Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 12/14] hw/virtio/virtio-blk.h: introduce VIRTIO_BLK_F_MQ, Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 13/14] hw/block/virtio-blk: create num_queues vqs if dataplane is enabled, Ming Lei, 2014/07/16
- [Qemu-devel] [PATCH RFC 14/14] dataplane: virtio-blk: support mutlti virtqueue,
Ming Lei <=