aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMichael S. Tsirkin <mst@redhat.com>2012-01-12 00:14:44 -0500
committerRusty Russell <rusty@rustcorp.com.au>2012-01-12 00:14:44 -0500
commit4678d6f970c2f7c0cbfefc0cc666432d153b321b (patch)
tree6757c596f991c36b38e4d6bb29df4ad0f49c330d /drivers
parente93300b1afc7cd4fe1e741ceaf06714d060e88b8 (diff)
virtio_blk: fix config handler race
Fix a theoretical race related to config work handler: a config interrupt might happen after we flush config work but before we reset the device. It will then cause the config work to run during or after reset. Two problems with this: - if this runs after device is gone we will get use after free - access of config while reset is in progress is racy (as layout is changing). As a solution 1. flush after reset when we know there will be no more interrupts 2. add a flag to disable config access before reset Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/virtio_blk.c22
1 files changed, 21 insertions, 1 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index a345e40e1bca..ba73661fb9f3 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -4,6 +4,7 @@
4#include <linux/blkdev.h> 4#include <linux/blkdev.h>
5#include <linux/hdreg.h> 5#include <linux/hdreg.h>
6#include <linux/module.h> 6#include <linux/module.h>
7#include <linux/mutex.h>
7#include <linux/virtio.h> 8#include <linux/virtio.h>
8#include <linux/virtio_blk.h> 9#include <linux/virtio_blk.h>
9#include <linux/scatterlist.h> 10#include <linux/scatterlist.h>
@@ -36,6 +37,12 @@ struct virtio_blk
36 /* Process context for config space updates */ 37 /* Process context for config space updates */
37 struct work_struct config_work; 38 struct work_struct config_work;
38 39
40 /* Lock for config space updates */
41 struct mutex config_lock;
42
43 /* enable config space updates */
44 bool config_enable;
45
39 /* What host tells us, plus 2 for header & tailer. */ 46 /* What host tells us, plus 2 for header & tailer. */
40 unsigned int sg_elems; 47 unsigned int sg_elems;
41 48
@@ -318,6 +325,10 @@ static void virtblk_config_changed_work(struct work_struct *work)
318 char cap_str_2[10], cap_str_10[10]; 325 char cap_str_2[10], cap_str_10[10];
319 u64 capacity, size; 326 u64 capacity, size;
320 327
328 mutex_lock(&vblk->config_lock);
329 if (!vblk->config_enable)
330 goto done;
331
321 /* Host must always specify the capacity. */ 332 /* Host must always specify the capacity. */
322 vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity), 333 vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
323 &capacity, sizeof(capacity)); 334 &capacity, sizeof(capacity));
@@ -340,6 +351,8 @@ static void virtblk_config_changed_work(struct work_struct *work)
340 cap_str_10, cap_str_2); 351 cap_str_10, cap_str_2);
341 352
342 set_capacity(vblk->disk, capacity); 353 set_capacity(vblk->disk, capacity);
354done:
355 mutex_unlock(&vblk->config_lock);
343} 356}
344 357
345static void virtblk_config_changed(struct virtio_device *vdev) 358static void virtblk_config_changed(struct virtio_device *vdev)
@@ -388,7 +401,9 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
388 vblk->vdev = vdev; 401 vblk->vdev = vdev;
389 vblk->sg_elems = sg_elems; 402 vblk->sg_elems = sg_elems;
390 sg_init_table(vblk->sg, vblk->sg_elems); 403 sg_init_table(vblk->sg, vblk->sg_elems);
404 mutex_init(&vblk->config_lock);
391 INIT_WORK(&vblk->config_work, virtblk_config_changed_work); 405 INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
406 vblk->config_enable = true;
392 407
393 /* We expect one virtqueue, for output. */ 408 /* We expect one virtqueue, for output. */
394 vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests"); 409 vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests");
@@ -542,7 +557,10 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
542 struct virtio_blk *vblk = vdev->priv; 557 struct virtio_blk *vblk = vdev->priv;
543 int index = vblk->index; 558 int index = vblk->index;
544 559
545 flush_work(&vblk->config_work); 560 /* Prevent config work handler from accessing the device. */
561 mutex_lock(&vblk->config_lock);
562 vblk->config_enable = false;
563 mutex_unlock(&vblk->config_lock);
546 564
547 /* Nothing should be pending. */ 565 /* Nothing should be pending. */
548 BUG_ON(!list_empty(&vblk->reqs)); 566 BUG_ON(!list_empty(&vblk->reqs));
@@ -550,6 +568,8 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
550 /* Stop all the virtqueues. */ 568 /* Stop all the virtqueues. */
551 vdev->config->reset(vdev); 569 vdev->config->reset(vdev);
552 570
571 flush_work(&vblk->config_work);
572
553 del_gendisk(vblk->disk); 573 del_gendisk(vblk->disk);
554 blk_cleanup_queue(vblk->disk->queue); 574 blk_cleanup_queue(vblk->disk->queue);
555 put_disk(vblk->disk); 575 put_disk(vblk->disk);