Add support for the new dynamic features config space field to allow
en/disabling the write cache at runtime.  The userspace interface is
a SCSI-compatible sysfs attribute.

Signed-off-by: Christoph Hellwig <h...@lst.de>

Index: linux-2.6/drivers/block/virtio_blk.c
===================================================================
--- linux-2.6.orig/drivers/block/virtio_blk.c   2011-03-15 12:16:29.156133695 
+0100
+++ linux-2.6/drivers/block/virtio_blk.c        2011-03-15 13:17:30.160634723 
+0100
@@ -291,6 +291,73 @@ static ssize_t virtblk_serial_show(struc
 }
 DEVICE_ATTR(serial, S_IRUGO, virtblk_serial_show, NULL);
 
+static bool virtblk_has_wb_cache(struct virtio_blk *vblk)
+{
+       struct virtio_device *vdev = vblk->vdev;
+       u32 features;
+
+       if (!virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
+               return false;
+       if (!virtio_has_feature(vdev, VIRTIO_BLK_F_DYNAMIC))
+               return true;
+
+       vdev->config->get(vdev, offsetof(struct virtio_blk_config, features),
+                         &features, sizeof(features));
+
+       if (features & VIRTIO_BLK_RT_WCE)
+               return true;
+       return false;
+}
+
+static ssize_t virtblk_cache_type_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct gendisk *disk = dev_to_disk(dev);
+
+       if (virtblk_has_wb_cache(disk->private_data))
+               return sprintf(buf, "write back\n");
+       else
+               return sprintf(buf, "write through\n");
+}
+
+static ssize_t virtblk_cache_type_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct gendisk *disk = dev_to_disk(dev);
+       struct virtio_blk *vblk = disk->private_data;
+       struct virtio_device *vdev = vblk->vdev;
+       u32 features, features2 = 0;
+
+       if (!virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH) ||
+           !virtio_has_feature(vdev, VIRTIO_BLK_F_DYNAMIC))
+               return -ENXIO;
+
+       if (strncmp(buf, "write through", sizeof("write through") - 1) == 0) {
+               ;
+       } else if (strncmp(buf, "write back", sizeof("write back") - 1) == 0) {
+               blk_queue_flush(disk->queue, REQ_FLUSH);
+               features |= VIRTIO_BLK_RT_WCE;
+       } else {
+               return -EINVAL;
+       }
+
+       vdev->config->set(vdev, offsetof(struct virtio_blk_config, features),
+                         &features, sizeof(features));
+
+       vdev->config->get(vdev, offsetof(struct virtio_blk_config, features),
+                         &features2, sizeof(features2));
+
+       if ((features & VIRTIO_BLK_RT_WCE) !=
+           (features2 & VIRTIO_BLK_RT_WCE))
+               return -EIO;
+
+       if (!(features2 & VIRTIO_BLK_RT_WCE))
+               blk_queue_flush(disk->queue, 0);
+       return count;
+}
+static DEVICE_ATTR(cache_type, S_IRUGO|S_IWUSR,
+           virtblk_cache_type_show, virtblk_cache_type_store);
+
 static int __devinit virtblk_probe(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk;
@@ -377,7 +444,7 @@ static int __devinit virtblk_probe(struc
        index++;
 
        /* configure queue flush support */
-       if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
+       if (virtblk_has_wb_cache(vblk))
                blk_queue_flush(q, REQ_FLUSH);
 
        /* If disk is read-only in the host, the guest should obey */
@@ -456,6 +523,10 @@ static int __devinit virtblk_probe(struc
        if (err)
                goto out_del_disk;
 
+       err = device_create_file(disk_to_dev(vblk->disk), &dev_attr_cache_type);
+       if (err)
+               goto out_del_disk;
+
        return 0;
 
 out_del_disk:
@@ -499,7 +570,7 @@ static const struct virtio_device_id id_
 static unsigned int features[] = {
        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_FLUSH, VIRTIO_BLK_F_TOPOLOGY
+       VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_DYNAMIC
 };
 
 /*
Index: linux-2.6/include/linux/virtio_blk.h
===================================================================
--- linux-2.6.orig/include/linux/virtio_blk.h   2011-03-15 12:14:28.261632780 
+0100
+++ linux-2.6/include/linux/virtio_blk.h        2011-03-15 12:25:56.308634399 
+0100
@@ -16,6 +16,7 @@
 #define VIRTIO_BLK_F_SCSI      7       /* Supports scsi command passthru */
 #define VIRTIO_BLK_F_FLUSH     9       /* Cache flush command support */
 #define VIRTIO_BLK_F_TOPOLOGY  10      /* Topology information is available */
+#define VIRTIO_BLK_F_DYNAMIC   11      /* Dynamic features field */
 
 #define VIRTIO_BLK_ID_BYTES    20      /* ID string length */
 
@@ -45,6 +46,9 @@ struct virtio_blk_config {
        __u16 min_io_size;
        /* optimal sustained I/O size in logical blocks. */
        __u32 opt_io_size;
+       /* runtime controllable features */
+       __u32 features;
+#define VIRTIO_BLK_RT_WCE              (1 << 0)
 
 } __attribute__((packed));
 

Reply via email to