|
@@ -395,6 +395,83 @@ static int virtblk_name_format(char *prefix, int index, char *buf, int buflen)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int virtblk_get_cache_mode(struct virtio_device *vdev)
|
|
|
+{
|
|
|
+ u8 writeback;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_CONFIG_WCE,
|
|
|
+ offsetof(struct virtio_blk_config, wce),
|
|
|
+ &writeback);
|
|
|
+ if (err)
|
|
|
+ writeback = virtio_has_feature(vdev, VIRTIO_BLK_F_WCE);
|
|
|
+
|
|
|
+ return writeback;
|
|
|
+}
|
|
|
+
|
|
|
+static void virtblk_update_cache_mode(struct virtio_device *vdev)
|
|
|
+{
|
|
|
+ u8 writeback = virtblk_get_cache_mode(vdev);
|
|
|
+ struct virtio_blk *vblk = vdev->priv;
|
|
|
+
|
|
|
+ if (writeback)
|
|
|
+ blk_queue_flush(vblk->disk->queue, REQ_FLUSH);
|
|
|
+ else
|
|
|
+ blk_queue_flush(vblk->disk->queue, 0);
|
|
|
+
|
|
|
+ revalidate_disk(vblk->disk);
|
|
|
+}
|
|
|
+
|
|
|
+static const char *const virtblk_cache_types[] = {
|
|
|
+ "write through", "write back"
|
|
|
+};
|
|
|
+
|
|
|
+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;
|
|
|
+ int i;
|
|
|
+ u8 writeback;
|
|
|
+
|
|
|
+ BUG_ON(!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_CONFIG_WCE));
|
|
|
+ for (i = ARRAY_SIZE(virtblk_cache_types); --i >= 0; )
|
|
|
+ if (sysfs_streq(buf, virtblk_cache_types[i]))
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (i < 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ writeback = i;
|
|
|
+ vdev->config->set(vdev,
|
|
|
+ offsetof(struct virtio_blk_config, wce),
|
|
|
+ &writeback, sizeof(writeback));
|
|
|
+
|
|
|
+ virtblk_update_cache_mode(vdev);
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t
|
|
|
+virtblk_cache_type_show(struct device *dev, struct device_attribute *attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ struct gendisk *disk = dev_to_disk(dev);
|
|
|
+ struct virtio_blk *vblk = disk->private_data;
|
|
|
+ u8 writeback = virtblk_get_cache_mode(vblk->vdev);
|
|
|
+
|
|
|
+ BUG_ON(writeback >= ARRAY_SIZE(virtblk_cache_types));
|
|
|
+ return snprintf(buf, 40, "%s\n", virtblk_cache_types[writeback]);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct device_attribute dev_attr_cache_type_ro =
|
|
|
+ __ATTR(cache_type, S_IRUGO,
|
|
|
+ virtblk_cache_type_show, NULL);
|
|
|
+static const struct device_attribute dev_attr_cache_type_rw =
|
|
|
+ __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;
|
|
@@ -471,8 +548,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
|
|
vblk->index = index;
|
|
|
|
|
|
/* configure queue flush support */
|
|
|
- if (virtio_has_feature(vdev, VIRTIO_BLK_F_FLUSH))
|
|
|
- blk_queue_flush(q, REQ_FLUSH);
|
|
|
+ virtblk_update_cache_mode(vdev);
|
|
|
|
|
|
/* If disk is read-only in the host, the guest should obey */
|
|
|
if (virtio_has_feature(vdev, VIRTIO_BLK_F_RO))
|
|
@@ -550,6 +626,14 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
|
|
if (err)
|
|
|
goto out_del_disk;
|
|
|
|
|
|
+ if (virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE))
|
|
|
+ err = device_create_file(disk_to_dev(vblk->disk),
|
|
|
+ &dev_attr_cache_type_rw);
|
|
|
+ else
|
|
|
+ err = device_create_file(disk_to_dev(vblk->disk),
|
|
|
+ &dev_attr_cache_type_ro);
|
|
|
+ if (err)
|
|
|
+ goto out_del_disk;
|
|
|
return 0;
|
|
|
|
|
|
out_del_disk:
|
|
@@ -642,7 +726,7 @@ static const struct virtio_device_id id_table[] = {
|
|
|
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_WCE, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE
|
|
|
};
|
|
|
|
|
|
/*
|