diff options
author | Christoph Hellwig <hch@lst.de> | 2011-02-01 15:43:48 -0500 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2011-05-29 21:44:13 -0400 |
commit | 7a7c924cf03da2a76ea4dc0aac1a788cf95a9c29 (patch) | |
tree | 717aba1aab456d252c684abf256afa8e92b7dd96 /drivers/block | |
parent | 990c91f0af46c57f0291060d928c7ab82f9d5667 (diff) |
virtio_blk: allow re-reading config space at runtime
Wire up the virtio_driver config_changed method to get notified about
config changes raised by the host. For now we just re-read the device
size to support online resizing of devices, but once we add more
attributes that might be changeable they could be added as well.
Note that the config_changed method is called from irq context, so
we'll have to use the workqueue infrastructure to provide us a proper
user context for our changes.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/virtio_blk.c | 88 |
1 files changed, 78 insertions, 10 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 6ecf89cdf006..33a48a80c7e8 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c | |||
@@ -6,10 +6,12 @@ | |||
6 | #include <linux/virtio.h> | 6 | #include <linux/virtio.h> |
7 | #include <linux/virtio_blk.h> | 7 | #include <linux/virtio_blk.h> |
8 | #include <linux/scatterlist.h> | 8 | #include <linux/scatterlist.h> |
9 | #include <linux/string_helpers.h> | ||
9 | 10 | ||
10 | #define PART_BITS 4 | 11 | #define PART_BITS 4 |
11 | 12 | ||
12 | static int major, index; | 13 | static int major, index; |
14 | struct workqueue_struct *virtblk_wq; | ||
13 | 15 | ||
14 | struct virtio_blk | 16 | struct virtio_blk |
15 | { | 17 | { |
@@ -26,6 +28,9 @@ struct virtio_blk | |||
26 | 28 | ||
27 | mempool_t *pool; | 29 | mempool_t *pool; |
28 | 30 | ||
31 | /* Process context for config space updates */ | ||
32 | struct work_struct config_work; | ||
33 | |||
29 | /* What host tells us, plus 2 for header & tailer. */ | 34 | /* What host tells us, plus 2 for header & tailer. */ |
30 | unsigned int sg_elems; | 35 | unsigned int sg_elems; |
31 | 36 | ||
@@ -291,6 +296,46 @@ static ssize_t virtblk_serial_show(struct device *dev, | |||
291 | } | 296 | } |
292 | DEVICE_ATTR(serial, S_IRUGO, virtblk_serial_show, NULL); | 297 | DEVICE_ATTR(serial, S_IRUGO, virtblk_serial_show, NULL); |
293 | 298 | ||
299 | static void virtblk_config_changed_work(struct work_struct *work) | ||
300 | { | ||
301 | struct virtio_blk *vblk = | ||
302 | container_of(work, struct virtio_blk, config_work); | ||
303 | struct virtio_device *vdev = vblk->vdev; | ||
304 | struct request_queue *q = vblk->disk->queue; | ||
305 | char cap_str_2[10], cap_str_10[10]; | ||
306 | u64 capacity, size; | ||
307 | |||
308 | /* Host must always specify the capacity. */ | ||
309 | vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity), | ||
310 | &capacity, sizeof(capacity)); | ||
311 | |||
312 | /* If capacity is too big, truncate with warning. */ | ||
313 | if ((sector_t)capacity != capacity) { | ||
314 | dev_warn(&vdev->dev, "Capacity %llu too large: truncating\n", | ||
315 | (unsigned long long)capacity); | ||
316 | capacity = (sector_t)-1; | ||
317 | } | ||
318 | |||
319 | size = capacity * queue_logical_block_size(q); | ||
320 | string_get_size(size, STRING_UNITS_2, cap_str_2, sizeof(cap_str_2)); | ||
321 | string_get_size(size, STRING_UNITS_10, cap_str_10, sizeof(cap_str_10)); | ||
322 | |||
323 | dev_notice(&vdev->dev, | ||
324 | "new size: %llu %d-byte logical blocks (%s/%s)\n", | ||
325 | (unsigned long long)capacity, | ||
326 | queue_logical_block_size(q), | ||
327 | cap_str_10, cap_str_2); | ||
328 | |||
329 | set_capacity(vblk->disk, capacity); | ||
330 | } | ||
331 | |||
332 | static void virtblk_config_changed(struct virtio_device *vdev) | ||
333 | { | ||
334 | struct virtio_blk *vblk = vdev->priv; | ||
335 | |||
336 | queue_work(virtblk_wq, &vblk->config_work); | ||
337 | } | ||
338 | |||
294 | static int __devinit virtblk_probe(struct virtio_device *vdev) | 339 | static int __devinit virtblk_probe(struct virtio_device *vdev) |
295 | { | 340 | { |
296 | struct virtio_blk *vblk; | 341 | struct virtio_blk *vblk; |
@@ -327,6 +372,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) | |||
327 | vblk->vdev = vdev; | 372 | vblk->vdev = vdev; |
328 | vblk->sg_elems = sg_elems; | 373 | vblk->sg_elems = sg_elems; |
329 | sg_init_table(vblk->sg, vblk->sg_elems); | 374 | sg_init_table(vblk->sg, vblk->sg_elems); |
375 | INIT_WORK(&vblk->config_work, virtblk_config_changed_work); | ||
330 | 376 | ||
331 | /* We expect one virtqueue, for output. */ | 377 | /* We expect one virtqueue, for output. */ |
332 | vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests"); | 378 | vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests"); |
@@ -477,6 +523,8 @@ static void __devexit virtblk_remove(struct virtio_device *vdev) | |||
477 | { | 523 | { |
478 | struct virtio_blk *vblk = vdev->priv; | 524 | struct virtio_blk *vblk = vdev->priv; |
479 | 525 | ||
526 | flush_work(&vblk->config_work); | ||
527 | |||
480 | /* Nothing should be pending. */ | 528 | /* Nothing should be pending. */ |
481 | BUG_ON(!list_empty(&vblk->reqs)); | 529 | BUG_ON(!list_empty(&vblk->reqs)); |
482 | 530 | ||
@@ -508,27 +556,47 @@ static unsigned int features[] = { | |||
508 | * Use __refdata to avoid this warning. | 556 | * Use __refdata to avoid this warning. |
509 | */ | 557 | */ |
510 | static struct virtio_driver __refdata virtio_blk = { | 558 | static struct virtio_driver __refdata virtio_blk = { |
511 | .feature_table = features, | 559 | .feature_table = features, |
512 | .feature_table_size = ARRAY_SIZE(features), | 560 | .feature_table_size = ARRAY_SIZE(features), |
513 | .driver.name = KBUILD_MODNAME, | 561 | .driver.name = KBUILD_MODNAME, |
514 | .driver.owner = THIS_MODULE, | 562 | .driver.owner = THIS_MODULE, |
515 | .id_table = id_table, | 563 | .id_table = id_table, |
516 | .probe = virtblk_probe, | 564 | .probe = virtblk_probe, |
517 | .remove = __devexit_p(virtblk_remove), | 565 | .remove = __devexit_p(virtblk_remove), |
566 | .config_changed = virtblk_config_changed, | ||
518 | }; | 567 | }; |
519 | 568 | ||
520 | static int __init init(void) | 569 | static int __init init(void) |
521 | { | 570 | { |
571 | int error; | ||
572 | |||
573 | virtblk_wq = alloc_workqueue("virtio-blk", 0, 0); | ||
574 | if (!virtblk_wq) | ||
575 | return -ENOMEM; | ||
576 | |||
522 | major = register_blkdev(0, "virtblk"); | 577 | major = register_blkdev(0, "virtblk"); |
523 | if (major < 0) | 578 | if (major < 0) { |
524 | return major; | 579 | error = major; |
525 | return register_virtio_driver(&virtio_blk); | 580 | goto out_destroy_workqueue; |
581 | } | ||
582 | |||
583 | error = register_virtio_driver(&virtio_blk); | ||
584 | if (error) | ||
585 | goto out_unregister_blkdev; | ||
586 | return 0; | ||
587 | |||
588 | out_unregister_blkdev: | ||
589 | unregister_blkdev(major, "virtblk"); | ||
590 | out_destroy_workqueue: | ||
591 | destroy_workqueue(virtblk_wq); | ||
592 | return error; | ||
526 | } | 593 | } |
527 | 594 | ||
528 | static void __exit fini(void) | 595 | static void __exit fini(void) |
529 | { | 596 | { |
530 | unregister_blkdev(major, "virtblk"); | 597 | unregister_blkdev(major, "virtblk"); |
531 | unregister_virtio_driver(&virtio_blk); | 598 | unregister_virtio_driver(&virtio_blk); |
599 | destroy_workqueue(virtblk_wq); | ||
532 | } | 600 | } |
533 | module_init(init); | 601 | module_init(init); |
534 | module_exit(fini); | 602 | module_exit(fini); |