|
@@ -4,6 +4,7 @@
|
|
|
#include <linux/blkdev.h>
|
|
|
#include <linux/hdreg.h>
|
|
|
#include <linux/module.h>
|
|
|
+#include <linux/mutex.h>
|
|
|
#include <linux/virtio.h>
|
|
|
#include <linux/virtio_blk.h>
|
|
|
#include <linux/scatterlist.h>
|
|
@@ -36,6 +37,12 @@ struct virtio_blk
|
|
|
/* Process context for config space updates */
|
|
|
struct work_struct config_work;
|
|
|
|
|
|
+ /* Lock for config space updates */
|
|
|
+ struct mutex config_lock;
|
|
|
+
|
|
|
+ /* enable config space updates */
|
|
|
+ bool config_enable;
|
|
|
+
|
|
|
/* What host tells us, plus 2 for header & tailer. */
|
|
|
unsigned int sg_elems;
|
|
|
|
|
@@ -318,6 +325,10 @@ static void virtblk_config_changed_work(struct work_struct *work)
|
|
|
char cap_str_2[10], cap_str_10[10];
|
|
|
u64 capacity, size;
|
|
|
|
|
|
+ mutex_lock(&vblk->config_lock);
|
|
|
+ if (!vblk->config_enable)
|
|
|
+ goto done;
|
|
|
+
|
|
|
/* Host must always specify the capacity. */
|
|
|
vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
|
|
|
&capacity, sizeof(capacity));
|
|
@@ -340,6 +351,8 @@ static void virtblk_config_changed_work(struct work_struct *work)
|
|
|
cap_str_10, cap_str_2);
|
|
|
|
|
|
set_capacity(vblk->disk, capacity);
|
|
|
+done:
|
|
|
+ mutex_unlock(&vblk->config_lock);
|
|
|
}
|
|
|
|
|
|
static void virtblk_config_changed(struct virtio_device *vdev)
|
|
@@ -388,7 +401,9 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
|
|
vblk->vdev = vdev;
|
|
|
vblk->sg_elems = sg_elems;
|
|
|
sg_init_table(vblk->sg, vblk->sg_elems);
|
|
|
+ mutex_init(&vblk->config_lock);
|
|
|
INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
|
|
|
+ vblk->config_enable = true;
|
|
|
|
|
|
/* We expect one virtqueue, for output. */
|
|
|
vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests");
|
|
@@ -542,7 +557,10 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
|
|
|
struct virtio_blk *vblk = vdev->priv;
|
|
|
int index = vblk->index;
|
|
|
|
|
|
- flush_work(&vblk->config_work);
|
|
|
+ /* Prevent config work handler from accessing the device. */
|
|
|
+ mutex_lock(&vblk->config_lock);
|
|
|
+ vblk->config_enable = false;
|
|
|
+ mutex_unlock(&vblk->config_lock);
|
|
|
|
|
|
/* Nothing should be pending. */
|
|
|
BUG_ON(!list_empty(&vblk->reqs));
|
|
@@ -550,6 +568,8 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
|
|
|
/* Stop all the virtqueues. */
|
|
|
vdev->config->reset(vdev);
|
|
|
|
|
|
+ flush_work(&vblk->config_work);
|
|
|
+
|
|
|
del_gendisk(vblk->disk);
|
|
|
blk_cleanup_queue(vblk->disk->queue);
|
|
|
put_disk(vblk->disk);
|