From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=39618 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OD1nU-0006Ly-3u for qemu-devel@nongnu.org; Fri, 14 May 2010 16:47:53 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OD1nR-0004z6-VG for qemu-devel@nongnu.org; Fri, 14 May 2010 16:47:52 -0400 Received: from mtagate6.de.ibm.com ([195.212.17.166]:47261) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OD1nR-0004yx-NH for qemu-devel@nongnu.org; Fri, 14 May 2010 16:47:49 -0400 Received: from d12nrmr1607.megacenter.de.ibm.com (d12nrmr1607.megacenter.de.ibm.com [9.149.167.49]) by mtagate6.de.ibm.com (8.13.1/8.13.1) with ESMTP id o4EKll9Y020397 for ; Fri, 14 May 2010 20:47:47 GMT Received: from d12av02.megacenter.de.ibm.com (d12av02.megacenter.de.ibm.com [9.149.165.228]) by d12nrmr1607.megacenter.de.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id o4EKllvq1560752 for ; Fri, 14 May 2010 22:47:47 +0200 Received: from d12av02.megacenter.de.ibm.com (loopback [127.0.0.1]) by d12av02.megacenter.de.ibm.com (8.12.11.20060308/8.13.3) with ESMTP id o4EKlkGu005319 for ; Fri, 14 May 2010 22:47:47 +0200 From: Stefan Hajnoczi Date: Fri, 14 May 2010 21:47:37 +0100 Message-Id: <1273870057-4287-1-git-send-email-stefanha@linux.vnet.ibm.com> Subject: [Qemu-devel] [PATCH RFC] virtio_blk: Use blk-iopoll for host->guest notify List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: virtualization@lists.linux-foundation.org Cc: Stefan Hajnoczi , kvm@vger.kernel.org, Rusty Russell , qemu-devel@nongnu.org, linux-kernel@vger.kernel.org, Jens Axboe This patch adds blk-iopoll interrupt mitigation to virtio-blk. Instead of processing completed requests inside the virtqueue interrupt handler, a softirq is scheduled to process up to a maximum number of completed requests in one go. If the number of complete requests exceeds the maximum number, then another softirq is scheduled to continue polling. Otherwise the virtqueue interrupt is enabled again and we return to interrupt-driven mode. The patch sets the maximum number of completed requests (aka budget, aka weight) to 4. This is a low number but reflects the expensive context switch between guest and host virtio-blk emulation. The blk-iopoll infrastructure is enabled system-wide by default: kernel.blk_iopoll = 1 It can be disabled to always use interrupt-driven mode (useful for comparison): kernel.blk_iopoll = 0 Signed-off-by: Stefan Hajnoczi --- No performance figures yet. drivers/block/virtio_blk.c | 71 ++++++++++++++++++++++++++++++++++++++----- 1 files changed, 62 insertions(+), 9 deletions(-) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 2138a7a..1523895 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -6,6 +6,7 @@ #include #include #include +#include #define PART_BITS 4 @@ -26,6 +27,9 @@ struct virtio_blk mempool_t *pool; + /* Host->guest notify mitigation */ + struct blk_iopoll iopoll; + /* What host tells us, plus 2 for header & tailer. */ unsigned int sg_elems; @@ -42,16 +46,18 @@ struct virtblk_req u8 status; }; -static void blk_done(struct virtqueue *vq) +/* Assumes vblk->lock held */ +static int __virtblk_end_requests(struct virtio_blk *vblk, int weight) { - struct virtio_blk *vblk = vq->vdev->priv; struct virtblk_req *vbr; unsigned int len; - unsigned long flags; + int error; + int work = 0; - spin_lock_irqsave(&vblk->lock, flags); - while ((vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len)) != NULL) { - int error; + while (!weight || work < weight) { + vbr = vblk->vq->vq_ops->get_buf(vblk->vq, &len); + if (!vbr) + break; switch (vbr->status) { case VIRTIO_BLK_S_OK: @@ -74,10 +80,53 @@ static void blk_done(struct virtqueue *vq) __blk_end_request_all(vbr->req, error); list_del(&vbr->list); mempool_free(vbr, vblk->pool); + work++; } + /* In case queue is stopped waiting for more buffers. */ blk_start_queue(vblk->disk->queue); + return work; +} + +static int virtblk_iopoll(struct blk_iopoll *iopoll, int weight) +{ + struct virtio_blk *vblk = + container_of(iopoll, struct virtio_blk, iopoll); + unsigned long flags; + int work; + + spin_lock_irqsave(&vblk->lock, flags); + + work = __virtblk_end_requests(vblk, weight); + if (work < weight) { + /* Keep polling if there are pending requests. */ + if (vblk->vq->vq_ops->enable_cb(vblk->vq)) + __blk_iopoll_complete(&vblk->iopoll); + else + vblk->vq->vq_ops->disable_cb(vblk->vq); + } + spin_unlock_irqrestore(&vblk->lock, flags); + return work; +} + +static void blk_done(struct virtqueue *vq) +{ + struct virtio_blk *vblk = vq->vdev->priv; + unsigned long flags; + + if (blk_iopoll_enabled) { + if (!blk_iopoll_sched_prep(&vblk->iopoll)) { + spin_lock_irqsave(&vblk->lock, flags); + vblk->vq->vq_ops->disable_cb(vblk->vq); + spin_unlock_irqrestore(&vblk->lock, flags); + blk_iopoll_sched(&vblk->iopoll); + } + } else { + spin_lock_irqsave(&vblk->lock, flags); + __virtblk_end_requests(vblk, 0); + spin_unlock_irqrestore(&vblk->lock, flags); + } } static bool do_req(struct request_queue *q, struct virtio_blk *vblk, @@ -289,11 +338,14 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) goto out_free_vq; } + blk_iopoll_init(&vblk->iopoll, 4 /* budget */, virtblk_iopoll); + blk_iopoll_enable(&vblk->iopoll); + /* FIXME: How many partitions? How long is a piece of string? */ vblk->disk = alloc_disk(1 << PART_BITS); if (!vblk->disk) { err = -ENOMEM; - goto out_mempool; + goto out_iopoll; } q = vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock); @@ -401,13 +453,13 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) if (!err && opt_io_size) blk_queue_io_opt(q, blk_size * opt_io_size); - add_disk(vblk->disk); return 0; out_put_disk: put_disk(vblk->disk); -out_mempool: +out_iopoll: + blk_iopoll_disable(&vblk->iopoll); mempool_destroy(vblk->pool); out_free_vq: vdev->config->del_vqs(vdev); @@ -430,6 +482,7 @@ static void __devexit virtblk_remove(struct virtio_device *vdev) del_gendisk(vblk->disk); blk_cleanup_queue(vblk->disk->queue); put_disk(vblk->disk); + blk_iopoll_disable(&vblk->iopoll); mempool_destroy(vblk->pool); vdev->config->del_vqs(vdev); kfree(vblk); -- 1.7.1