Commit f1b0ef06 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Rusty Russell
Browse files

virtio_blk: add support for cache flush



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.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 3ca4f5ca
Loading
Loading
Loading
Loading
+25 −5
Original line number Diff line number Diff line
@@ -92,15 +92,26 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
		return false;

	vbr->req = req;
	if (blk_fs_request(vbr->req)) {
	switch (req->cmd_type) {
	case REQ_TYPE_FS:
		vbr->out_hdr.type = 0;
		vbr->out_hdr.sector = blk_rq_pos(vbr->req);
		vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
	} else if (blk_pc_request(vbr->req)) {
		break;
	case REQ_TYPE_BLOCK_PC:
		vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD;
		vbr->out_hdr.sector = 0;
		vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
	} else {
		break;
	case REQ_TYPE_LINUX_BLOCK:
		if (req->cmd[0] == REQ_LB_OP_FLUSH) {
			vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
			vbr->out_hdr.sector = 0;
			vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
			break;
		}
		/*FALLTHRU*/
	default:
		/* We don't put anything else in the queue. */
		BUG();
	}
@@ -200,6 +211,12 @@ out:
	return err;
}

static void virtblk_prepare_flush(struct request_queue *q, struct request *req)
{
	req->cmd_type = REQ_TYPE_LINUX_BLOCK;
	req->cmd[0] = REQ_LB_OP_FLUSH;
}

static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
			 unsigned cmd, unsigned long data)
{
@@ -338,7 +355,10 @@ static int __devinit 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 (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 */
@@ -425,7 +445,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_SCSI, VIRTIO_BLK_F_IDENTIFY
	VIRTIO_BLK_F_SCSI, VIRTIO_BLK_F_IDENTIFY, VIRTIO_BLK_F_FLUSH
};

/*
+15 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#define VIRTIO_BLK_F_BLK_SIZE	6	/* Block size of disk is available*/
#define VIRTIO_BLK_F_SCSI	7	/* Supports scsi command passthru */
#define VIRTIO_BLK_F_IDENTIFY	8	/* ATA IDENTIFY supported */
#define VIRTIO_BLK_F_FLUSH	9	/* Cache flush command support */

#define VIRTIO_BLK_ID_BYTES	(sizeof(__u16[256]))	/* IDENTIFY DATA */

@@ -35,6 +36,17 @@ struct virtio_blk_config {
	__u8 identify[VIRTIO_BLK_ID_BYTES];
} __attribute__((packed));

/*
 * Command types
 *
 * Usage is a bit tricky as some bits are used as flags and some are not.
 *
 * Rules:
 *   VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or
 *   VIRTIO_BLK_T_BARRIER.  VIRTIO_BLK_T_FLUSH is a command of its own
 *   and may not be combined with any of the other flags.
 */

/* These two define direction. */
#define VIRTIO_BLK_T_IN		0
#define VIRTIO_BLK_T_OUT	1
@@ -42,6 +54,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