From: Christoph Hellwig <chellwig@redhat.com> Date: Fri, 7 May 2010 10:31:24 -0400 Subject: [virt] virtio_blk: add support for cache flushes Message-id: <1273228284-23772-1-git-send-email-chellwig@redhat.com> Patchwork-id: 24889 O-Subject: virtio_blk: add support for cache flushes Bugzilla: 571735 RH-Acked-by: Jes Sorensen <Jes.Sorensen@redhat.com> Recent qemu has added a VIRTIO_BLK_F_FLUSH flag to advertise that the virtual disk has a volatile write cache that needs to be flushed. In case we see this feature implement tell the Linux block layer about the fact and use the new VIRTIO_BLK_T_FLUSH to flush the cache when required. This allows for an correct and simple implementation of write barriers. Formally this is a backport of: commit f1b0ef062602713c2c7cfa12362d5d90ed01c5f6 Author: Christoph Hellwig <hch@lst.de> Date: Thu Sep 17 19:57:42 2009 +0200 but the code is very different from mainline because the block layer in 2.6.18 misses the nice Linux internal command type that the cache flush support is using upstream. Instead we abuse an ata-internal command type which is ignored by the block layer and other drivers for our cache flush command. BZ: https://bugzilla.redhat.com/show_bug.cgi?id=571735 Signed-off-by: Jarod Wilson <jarod@redhat.com> diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index a102e31..f7eff0c 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -84,7 +84,12 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, return false; vbr->req = req; - if (blk_fs_request(vbr->req)) { + if (vbr->req->flags & REQ_DRIVE_TASK) { + BUG_ON(vbr->req->cmd[0] != VIRTIO_BLK_T_FLUSH); + vbr->out_hdr.type = vbr->req->cmd[0]; + vbr->out_hdr.sector = 0; + vbr->out_hdr.ioprio = vbr->req->ioprio; + } else if (blk_fs_request(vbr->req)) { vbr->out_hdr.type = 0; vbr->out_hdr.sector = vbr->req->sector; vbr->out_hdr.ioprio = vbr->req->ioprio; @@ -148,6 +153,17 @@ static void do_virtblk_request(struct request_queue *q) vblk->vq->vq_ops->kick(vblk->vq); } +static void virtblk_prepare_flush(struct request_queue *q, struct request *req) +{ + /* + * XXX: abuse the special IDE-internal commands as we don't have the + * generic LINUX_BLOCK commands used for this in mainline available + * on this old kernel. + */ + req->flags |= REQ_DRIVE_TASK; + req->cmd[0] = VIRTIO_BLK_T_FLUSH; +} + static int virtblk_ioctl(struct inode *inode, struct file *filp, unsigned cmd, unsigned long data) { @@ -260,7 +276,11 @@ static int virtblk_probe(struct virtio_device *vdev) index++; /* If barriers are supported, tell block layer that queue is ordered */ - if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) + /* If barriers are supported, tell block layer that queue is ordered */ + if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH)) + blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_DRAIN_FLUSH, + virtblk_prepare_flush); + else if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); /* If disk is read-only in the host, the guest should obey */ @@ -341,6 +361,7 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX, VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE, + VIRTIO_BLK_F_FLUSH }; static struct virtio_driver virtio_blk = { diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index c1aef85..4f2f355 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -14,6 +14,7 @@ #define VIRTIO_BLK_F_GEOMETRY 4 /* Legacy geometry available */ #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ +#define VIRTIO_BLK_F_FLUSH 9 /* Cache flush command support */ struct virtio_blk_config { @@ -40,6 +41,9 @@ struct virtio_blk_config /* This bit says it's a scsi command, not an actual read or write. */ #define VIRTIO_BLK_T_SCSI_CMD 2 +/* Cache flush command */ +#define VIRTIO_BLK_T_FLUSH 4 + /* Barrier before this op. */ #define VIRTIO_BLK_T_BARRIER 0x80000000