virtio_blk: allow re-reading config space at runtime
Subsystems (4)
PATCH 1/1 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.
drivers/block/virtio_blk.c +88 -10
@@ -6,10 +6,12 @@#include <linux/virtio.h> #include <linux/virtio_blk.h> #include <linux/scatterlist.h> +#include <linux/string_helpers.h> #define PART_BITS 4static int major, index; +struct workqueue_struct *virtblk_wq;struct virtio_blk {
@@ -42,6 +44,11 @@ struct virtblk_requ8 status; }; +struct virtblk_config_change { +struct virtio_device *vdev; +struct work_struct work; +}; +static void blk_done (struct virtqueue *vq) {struct virtio_blk *vblk = vq->vdev->priv;
@@ -291,6 +298,57 @@ static ssize_t virtblk_serial_show(struc }DEVICE_ATTR (serial,S_IRUGO , virtblk_serial_show,NULL ); +static void virtblk_config_changed_work (struct work_struct *work) +{ +struct virtblk_config_change *cfg = + container_of(work, struct virtblk_config_change, work); +struct virtio_device *vdev = cfg->vdev; +struct virtio_blk *vblk = vdev->priv; +struct request_queue *q = vblk->disk->queue; +char cap_str_2[10 ], cap_str_10[10 ]; +u64 capacity, size; + +/* Host must always specify the capacity. */ + vdev->config->get (vdev, offsetof(struct virtio_blk_config , capacity), + &capacity,sizeof (capacity)); + +/* If capacity is too big, truncate with warning. */ +if ((sector_t )capacity != capacity) { +dev_warn (&vdev->dev,"Capacity %llu too large: truncating\n" , + (unsigned long long )capacity); + capacity = (sector_t)-1 ; + } + + size = capacity *queue_logical_block_size (q); +string_get_size (size,STRING_UNITS_2 , cap_str_2,sizeof (cap_str_2)); +string_get_size (size,STRING_UNITS_10 , cap_str_10,sizeof (cap_str_10)); + +dev_notice (&vdev->dev, +"new size: %llu %d-byte logical blocks (%s/%s)\n" , + (unsigned long long )capacity, +queue_logical_block_size (q), + cap_str_10, cap_str_2); + +set_capacity (vblk->disk, capacity); + +} + +static void virtblk_config_changed (struct virtio_device *vdev) +{ +struct virtblk_config_change *cfg; + + cfg =kmalloc (sizeof (*cfg),GFP_ATOMIC ); +if (!cfg) { +dev_info (&vdev->dev,"skipping config change\n" ); +return ; + } + + cfg->vdev = vdev; +INIT_WORK (&cfg->work, virtblk_config_changed_work); +queue_work (virtblk_wq, &cfg->work); +} + +static int __devinit virtblk_probe (struct virtio_device *vdev) {struct virtio_blk *vblk;
@@ -508,27 +566,47 @@ static unsigned int features[] = {* Use __refdata to avoid this warning. */ static struct virtio_driver __refdata virtio_blk = { - .feature_table = features, - .feature_table_size =ARRAY_SIZE (features), - .driver.name =KBUILD_MODNAME , - .driver.owner =THIS_MODULE , - .id_table = id_table, - .probe = virtblk_probe, - .remove =__devexit_p (virtblk_remove), + .feature_table = features, + .feature_table_size =ARRAY_SIZE (features), + .driver.name =KBUILD_MODNAME , + .driver.owner =THIS_MODULE , + .id_table = id_table, + .probe = virtblk_probe, + .remove =__devexit_p (virtblk_remove), + .config_changed = virtblk_config_changed, };static int __init init (void ) { +int error; + + virtblk_wq =alloc_workqueue ("md_misc" ,0 ,0 ); +if (!virtblk_wq) +return -ENOMEM ; + major =register_blkdev (0 ,"virtblk" ); -if (major <0 ) -return major; -return register_virtio_driver (&virtio_blk); +if (major <0 ) { + error = major; + goto out_destroy_workqueue; + } + + error =register_virtio_driver (&virtio_blk); +if (error) + goto out_unregister_blkdev; +return 0 ; + +out_unregister_blkdev: +unregister_blkdev (major,"virtblk" ); +out_destroy_workqueue: +destroy_workqueue (virtblk_wq); +return error; } static void __exit fini(void) {unregister_blkdev (major,"virtblk" );unregister_virtio_driver (&virtio_blk); +destroy_workqueue (virtblk_wq); }module_init (init);module_exit (fini);
Quoted 78 lines · Christoph Hellwig
Wire up the
virtio_driverconfig_changedmethod 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_changedmethod 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 <[email protected]>Index:
xfs/drivers/block/virtio_blk.c
===================================================================drivers/block/virtio_blk.c +41
-0@@ -6,10 +6,12 @@#include <linux/virtio.h> #include <linux/virtio_blk.h> #include <linux/scatterlist.h> +#include <linux/string_helpers.h> #define PART_BITS 4static int major, index; +struct workqueue_struct *virtblk_wq;struct virtio_blk {@@ -42,6 +44,11 @@ struct virtblk_requ8 status; }; +struct virtblk_config_change { +struct virtio_device *vdev; +struct work_struct work; +}; +static void blk_done (struct virtqueue *vq) {struct virtio_blk *vblk = vq->vdev->priv;@@ -291,6 +298,57 @@ static ssize_t virtblk_serial_show(struc }DEVICE_ATTR (serial,S_IRUGO , virtblk_serial_show,NULL ); +static void virtblk_config_changed_work (struct work_struct *work) +{ +struct virtblk_config_change *cfg = + container_of(work, struct virtblk_config_change, work); +struct virtio_device *vdev = cfg->vdev; +struct virtio_blk *vblk = vdev->priv; +struct request_queue *q = vblk->disk->queue; +char cap_str_2[10 ], cap_str_10[10 ]; +u64 capacity, size; + +/* Host must always specify the capacity. */ + vdev->config->get (vdev, offsetof(struct virtio_blk_config , capacity), + &capacity,sizeof (capacity)); + +/* If capacity is too big, truncate with warning. */ +if ((sector_t )capacity != capacity) { +dev_warn (&vdev->dev,"Capacity %llu too large: truncating\n" , + (unsigned long long )capacity); + capacity = (sector_t)-1 ; + } + + size = capacity *queue_logical_block_size (q); +string_get_size (size,STRING_UNITS_2 , cap_str_2,sizeof (cap_str_2)); +string_get_size (size,STRING_UNITS_10 , cap_str_10,sizeof (cap_str_10)); + +dev_notice (&vdev->dev, +"new size: %llu %d-byte logical blocks (%s/%s)\n" , + (unsigned long long )capacity, +queue_logical_block_size (q), + cap_str_10, cap_str_2); + +set_capacity (vblk->disk, capacity); + +}
drivers/block/virtio_blk.c static ssize_t virtblk_serial_show(struc · line 344
Quoted 10 lines
drivers/block/virtio_blk.c +10
-0+ +static void virtblk_config_changed (struct virtio_device *vdev) +{ +struct virtblk_config_change *cfg; + + cfg =kmalloc (sizeof (*cfg),GFP_ATOMIC ); +if (!cfg) { +dev_info (&vdev->dev,"skipping config change\n" ); +return ; + }
I think we need to do better than this. What would it take?
Rusty.
Quoted 1 line · Rusty Russell
I think we need to do better than this. What would it take?
I don't think we have much choice. We're getting called from irq context, so we'll need to allocate memory to offload things. That's unless we keep a thread around that we just need to wake up, which would be a huge waste of ressources.
Quoted 7 lines · Christoph Hellwig
Quoted 1 line · Rusty Russell
I think we need to do better than this. What would it take?
I don't think we have much choice. We're getting called from irq context, so we'll need to allocate memory to offload things. That's unless we keep a thread around that we just need to wake up, which would be a huge waste of ressources.
Yes, but can we keep a workqueue struct around?
Rusty.
Quoted 6 lines · Rusty Russell
Quoted 4 lines
I don't think we have much choice. We're getting called from irq context, so we'll need to allocate memory to offload things. That's unless we keep a thread around that we just need to wake up, which would be a huge waste of ressources.
Yes, but can we keep a workqueue struct around?
We can keep one around, but we can't prevent the virtio core code from calling us again while it's still in use.
Michael S. Tsirkin
maintainerQuoted 10 lines · Christoph Hellwig
Quoted 6 lines · Rusty Russell
Quoted 4 lines
I don't think we have much choice. We're getting called from irq context, so we'll need to allocate memory to offload things. That's unless we keep a thread around that we just need to wake up, which would be a huge waste of ressources.
Yes, but can we keep a workqueue struct around?
We can keep one around, but we can't prevent the virtio core code from calling us again while it's still in use.
Yes but in that specific situation (double schedule before it runs once) discarding one change notification would not hurt at all.
Quoted 6 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Quoted 3 lines · Michael S. Tsirkin
Yes but in that specific situation (double schedule before it runs once) discarding one change notification would not hurt at all.
Except that you can't easily do it. We can't simply reuse a work structure embedded in say struct virtio_blk without first doing a flush_work_sync/cancel_work_sync to remove it from the internal workqueue.c queues. And those calls can sleep and thus can't be called from interrupt context either.
Michael S. Tsirkin
maintainerQuoted 11 lines · Christoph Hellwig
Quoted 3 lines · Michael S. Tsirkin
Yes but in that specific situation (double schedule before it runs once) discarding one change notification would not hurt at all.
Except that you can't easily do it. We can't simply reuse a work structure embedded in say struct
virtio_blkwithout first doing a flush_work_sync/cancel_work_sync to remove it from the internalworkqueue.cqueues.
And those calls can sleep and thus can't be called
from interrupt context either.
Should not be hard to solve.
If it's running, it is ok to requeue. I don't remember offhand if it's ok to requeue if it's queued but not running, if not we could have a flag that signals that's the case.
Quoted 4 lines · Michael S. Tsirkin
Should not be hard to solve.
If it's running, it is ok to requeue. I don't remember offhand if it's ok to requeue if it's queued but not running, if not we could have a flag that signals that's the case.
Looks like the workqueue infrastructure does indeed handle requing an pending work_struct, and we're free to reuse it from the point just before the callback is called.
Does the version below look okay?
---
From: Christoph Hellwig <[email protected]>
Subject: [PATCH v2] 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.
drivers/block/virtio_blk.c +79 -10
@@ -6,10 +6,12 @@#include <linux/virtio.h> #include <linux/virtio_blk.h> #include <linux/scatterlist.h> +#include <linux/string_helpers.h> #define PART_BITS 4static int major, index; +struct workqueue_struct *virtblk_wq;struct virtio_blk {
@@ -26,6 +28,9 @@ struct virtio_blkmempool_t *pool; +/* Process context for config space updates */ +struct work_struct config_work; +/* What host tells us, plus 2 for header & tailer. */ unsigned int sg_elems;
@@ -291,6 +296,47 @@ static ssize_t virtblk_serial_show(struc }DEVICE_ATTR (serial,S_IRUGO , virtblk_serial_show,NULL ); +static void virtblk_config_changed_work (struct work_struct *work) +{ +struct virtio_blk *vblk = + container_of(work, struct virtio_blk, config_work); +struct virtio_device *vdev = vblk->vdev; +struct request_queue *q = vblk->disk->queue; +char cap_str_2[10 ], cap_str_10[10 ]; +u64 capacity, size; + +/* Host must always specify the capacity. */ + vdev->config->get (vdev, offsetof(struct virtio_blk_config , capacity), + &capacity,sizeof (capacity)); + +/* If capacity is too big, truncate with warning. */ +if ((sector_t )capacity != capacity) { +dev_warn (&vdev->dev,"Capacity %llu too large: truncating\n" , + (unsigned long long )capacity); + capacity = (sector_t)-1 ; + } + + size = capacity *queue_logical_block_size (q); +string_get_size (size,STRING_UNITS_2 , cap_str_2,sizeof (cap_str_2)); +string_get_size (size,STRING_UNITS_10 , cap_str_10,sizeof (cap_str_10)); + +dev_notice (&vdev->dev, +"new size: %llu %d-byte logical blocks (%s/%s)\n" , + (unsigned long long )capacity, +queue_logical_block_size (q), + cap_str_10, cap_str_2); + +set_capacity (vblk->disk, capacity); + +} + +static void virtblk_config_changed (struct virtio_device *vdev) +{ +struct virtio_blk *vblk = vdev->priv; + +queue_work (virtblk_wq, &vblk->config_work); +} +static int __devinit virtblk_probe (struct virtio_device *vdev) {struct virtio_blk *vblk;
@@ -327,6 +373,7 @@ static int __devinit virtblk_probe(struc vblk->vdev = vdev; vblk->sg_elems = sg_elems;sg_init_table (vblk->sg, vblk->sg_elems); +INIT_WORK (&vblk->config_work, virtblk_config_changed_work);/* We expect one virtqueue, for output. */ vblk->vq =virtio_find_single_vq (vdev, blk_done,"requests" );
@@ -477,6 +524,8 @@ static void __devexit virtblk_remove(str {struct virtio_blk *vblk = vdev->priv; +flush_work (&vblk->config_work); +/* Nothing should be pending. */ BUG_ON (!list_empty (&vblk->reqs));
@@ -508,27 +557,47 @@ static unsigned int features[] = {* Use __refdata to avoid this warning. */ static struct virtio_driver __refdata virtio_blk = { - .feature_table = features, - .feature_table_size =ARRAY_SIZE (features), - .driver.name =KBUILD_MODNAME , - .driver.owner =THIS_MODULE , - .id_table = id_table, - .probe = virtblk_probe, - .remove =__devexit_p (virtblk_remove), + .feature_table = features, + .feature_table_size =ARRAY_SIZE (features), + .driver.name =KBUILD_MODNAME , + .driver.owner =THIS_MODULE , + .id_table = id_table, + .probe = virtblk_probe, + .remove =__devexit_p (virtblk_remove), + .config_changed = virtblk_config_changed, };static int __init init (void ) { +int error; + + virtblk_wq =alloc_workqueue ("md_misc" ,0 ,0 ); +if (!virtblk_wq) +return -ENOMEM ; + major =register_blkdev (0 ,"virtblk" ); -if (major <0 ) -return major; -return register_virtio_driver (&virtio_blk); +if (major <0 ) { + error = major; + goto out_destroy_workqueue; + } + + error =register_virtio_driver (&virtio_blk); +if (error) + goto out_unregister_blkdev; +return 0 ; + +out_unregister_blkdev: +unregister_blkdev (major,"virtblk" ); +out_destroy_workqueue: +destroy_workqueue (virtblk_wq); +return error; } static void __exit fini(void) {unregister_blkdev (major,"virtblk" );unregister_virtio_driver (&virtio_blk); +destroy_workqueue (virtblk_wq); }module_init (init);module_exit (fini);
Quoted 11 lines · Christoph Hellwig
Quoted 4 lines · Michael S. Tsirkin
Should not be hard to solve.
If it's running, it is ok to requeue. I don't remember offhand if it's ok to requeue if it's queued but not running, if not we could have a flag that signals that's the case.Looks like the workqueue infrastructure does indeed handle requing an pending
work_struct, and we're free to reuse it from the point just before the callback is called.Does the version below look okay?
Yes, looks good to me. A couple of minor comments below but they don't really matter. FWIW
Quoted 54 lines
---
From: Christoph Hellwig <[email protected]>
Subject: [PATCH v2]virtio_blk: allow re-reading config space at runtimeWire up the
virtio_driverconfig_changedmethod 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_changedmethod 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 <[email protected]>Index:
linux-2.6/drivers/block/virtio_blk.c
===================================================================drivers/block/virtio_blk.c +13
-0@@ -6,10 +6,12 @@#include <linux/virtio.h> #include <linux/virtio_blk.h> #include <linux/scatterlist.h> +#include <linux/string_helpers.h> #define PART_BITS 4static int major, index; +struct workqueue_struct *virtblk_wq;struct virtio_blk {@@ -26,6 +28,9 @@ struct virtio_blkmempool_t *pool; +/* Process context for config space updates */ +struct work_struct config_work; +/* What host tells us, plus 2 for header & tailer. */ unsigned int sg_elems;@@ -291,6 +296,47 @@ static ssize_t virtblk_serial_show(struc }DEVICE_ATTR (serial,S_IRUGO , virtblk_serial_show,NULL ); +static void virtblk_config_changed_work (struct work_struct *work) +{ +struct virtio_blk *vblk = + container_of(work, struct virtio_blk, config_work); +struct virtio_device *vdev = vblk->vdev; +struct request_queue *q = vblk->disk->queue; +char cap_str_2[10 ], cap_str_10[10 ]; +u64 capacity, size;
drivers/block/virtio_blk.c static ssize_t virtblk_serial_show(struc · line 316
Quoted 10 lines
drivers/block/virtio_blk.c +10
-0+ +/* Host must always specify the capacity. */ + vdev->config->get (vdev, offsetof(struct virtio_blk_config , capacity), + &capacity,sizeof (capacity)); + +/* If capacity is too big, truncate with warning. */ +if ((sector_t )capacity != capacity) { +dev_warn (&vdev->dev,"Capacity %llu too large: truncating\n" , + (unsigned long long )capacity); + capacity = (sector_t)-1 ;
One thing to note here is that we will get this warning on each config change even if capacity is not updated. Not sure whether it matters.
Quoted 4 lines
drivers/block/virtio_blk.c +4
-0+ } + + size = capacity *queue_logical_block_size (q); +string_get_size (size,STRING_UNITS_2 , cap_str_2,sizeof (cap_str_2));
drivers/block/virtio_blk.c static ssize_t virtblk_serial_show(struc · line 330
Quoted 10 lines
drivers/block/virtio_blk.c +10
-0+string_get_size (size,STRING_UNITS_10 , cap_str_10,sizeof (cap_str_10)); + +dev_notice (&vdev->dev, +"new size: %llu %d-byte logical blocks (%s/%s)\n" , + (unsigned long long )capacity, +queue_logical_block_size (q), + cap_str_10, cap_str_2); + +set_capacity (vblk->disk, capacity); +
empty line :)
Quoted 45 lines
drivers/block/virtio_blk.c +17
-7+} + +static void virtblk_config_changed (struct virtio_device *vdev) +{ +struct virtio_blk *vblk = vdev->priv; + +queue_work (virtblk_wq, &vblk->config_work); +} +static int __devinit virtblk_probe (struct virtio_device *vdev) {struct virtio_blk *vblk;@@ -327,6 +373,7 @@ static int __devinit virtblk_probe(struc vblk->vdev = vdev; vblk->sg_elems = sg_elems;sg_init_table (vblk->sg, vblk->sg_elems); +INIT_WORK (&vblk->config_work, virtblk_config_changed_work);/* We expect one virtqueue, for output. */ vblk->vq =virtio_find_single_vq (vdev, blk_done,"requests" );@@ -477,6 +524,8 @@ static void __devexit virtblk_remove(str {struct virtio_blk *vblk = vdev->priv; +flush_work (&vblk->config_work); +/* Nothing should be pending. */ BUG_ON (!list_empty (&vblk->reqs));@@ -508,27 +557,47 @@ static unsigned int features[] = {* Use __refdata to avoid this warning. */ static struct virtio_driver __refdata virtio_blk = { - .feature_table = features, - .feature_table_size =ARRAY_SIZE (features), - .driver.name =KBUILD_MODNAME , - .driver.owner =THIS_MODULE , - .id_table = id_table, - .probe = virtblk_probe, - .remove =__devexit_p (virtblk_remove), + .feature_table = features, + .feature_table_size =ARRAY_SIZE (features), + .driver.name =KBUILD_MODNAME , + .driver.owner =THIS_MODULE , + .id_table = id_table,
drivers/block/virtio_blk.c static unsigned int features[] = { · line 574
Quoted 10 lines
drivers/block/virtio_blk.c +6
-0+ .probe = virtblk_probe, + .remove = __devexit_p(virtblk_remove), + .config_changed = virtblk_config_changed, };static int __init init (void ) { +int error; + + virtblk_wq =alloc_workqueue ("md_misc" ,0 ,0 );
What does md_misc stand for? Would virtblk_wq be a better name?
Quoted 32 lines
drivers/block/virtio_blk.c +19
-3+if (!virtblk_wq) +return -ENOMEM ; + major =register_blkdev (0 ,"virtblk" ); -if (major <0 ) -return major; -return register_virtio_driver (&virtio_blk); +if (major <0 ) { + error = major; + goto out_destroy_workqueue; + } + + error =register_virtio_driver (&virtio_blk); +if (error) + goto out_unregister_blkdev; +return 0 ; + +out_unregister_blkdev: +unregister_blkdev (major,"virtblk" ); +out_destroy_workqueue: +destroy_workqueue (virtblk_wq); +return error; } static void __exit fini(void) {unregister_blkdev (major,"virtblk" );unregister_virtio_driver (&virtio_blk); +destroy_workqueue (virtblk_wq); }module_init (init);module_exit (fini);
PATCH v3 1/1 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.
drivers/block/virtio_blk.c +78 -10
@@ -6,10 +6,12 @@#include <linux/virtio.h> #include <linux/virtio_blk.h> #include <linux/scatterlist.h> +#include <linux/string_helpers.h> #define PART_BITS 4static int major, index; +struct workqueue_struct *virtblk_wq;struct virtio_blk {
@@ -26,6 +28,9 @@ struct virtio_blkmempool_t *pool; +/* Process context for config space updates */ +struct work_struct config_work; +/* What host tells us, plus 2 for header & tailer. */ unsigned int sg_elems;
@@ -291,6 +296,46 @@ static ssize_t virtblk_serial_show(struc }DEVICE_ATTR (serial,S_IRUGO , virtblk_serial_show,NULL ); +static void virtblk_config_changed_work (struct work_struct *work) +{ +struct virtio_blk *vblk = + container_of(work, struct virtio_blk, config_work); +struct virtio_device *vdev = vblk->vdev; +struct request_queue *q = vblk->disk->queue; +char cap_str_2[10 ], cap_str_10[10 ]; +u64 capacity, size; + +/* Host must always specify the capacity. */ + vdev->config->get (vdev, offsetof(struct virtio_blk_config , capacity), + &capacity,sizeof (capacity)); + +/* If capacity is too big, truncate with warning. */ +if ((sector_t )capacity != capacity) { +dev_warn (&vdev->dev,"Capacity %llu too large: truncating\n" , + (unsigned long long )capacity); + capacity = (sector_t)-1 ; + } + + size = capacity *queue_logical_block_size (q); +string_get_size (size,STRING_UNITS_2 , cap_str_2,sizeof (cap_str_2)); +string_get_size (size,STRING_UNITS_10 , cap_str_10,sizeof (cap_str_10)); + +dev_notice (&vdev->dev, +"new size: %llu %d-byte logical blocks (%s/%s)\n" , + (unsigned long long )capacity, +queue_logical_block_size (q), + cap_str_10, cap_str_2); + +set_capacity (vblk->disk, capacity); +} + +static void virtblk_config_changed (struct virtio_device *vdev) +{ +struct virtio_blk *vblk = vdev->priv; + +queue_work (virtblk_wq, &vblk->config_work); +} +static int __devinit virtblk_probe (struct virtio_device *vdev) {struct virtio_blk *vblk;
@@ -327,6 +372,7 @@ static int __devinit virtblk_probe(struc vblk->vdev = vdev; vblk->sg_elems = sg_elems;sg_init_table (vblk->sg, vblk->sg_elems); +INIT_WORK (&vblk->config_work, virtblk_config_changed_work);/* We expect one virtqueue, for output. */ vblk->vq =virtio_find_single_vq (vdev, blk_done,"requests" );
@@ -477,6 +523,8 @@ static void __devexit virtblk_remove(str {struct virtio_blk *vblk = vdev->priv; +flush_work (&vblk->config_work); +/* Nothing should be pending. */ BUG_ON (!list_empty (&vblk->reqs));
@@ -508,27 +556,47 @@ static unsigned int features[] = {* Use __refdata to avoid this warning. */ static struct virtio_driver __refdata virtio_blk = { - .feature_table = features, - .feature_table_size =ARRAY_SIZE (features), - .driver.name =KBUILD_MODNAME , - .driver.owner =THIS_MODULE , - .id_table = id_table, - .probe = virtblk_probe, - .remove =__devexit_p (virtblk_remove), + .feature_table = features, + .feature_table_size =ARRAY_SIZE (features), + .driver.name =KBUILD_MODNAME , + .driver.owner =THIS_MODULE , + .id_table = id_table, + .probe = virtblk_probe, + .remove =__devexit_p (virtblk_remove), + .config_changed = virtblk_config_changed, };static int __init init (void ) { +int error; + + virtblk_wq =alloc_workqueue ("virtio-blk" ,0 ,0 ); +if (!virtblk_wq) +return -ENOMEM ; + major =register_blkdev (0 ,"virtblk" ); -if (major <0 ) -return major; -return register_virtio_driver (&virtio_blk); +if (major <0 ) { + error = major; + goto out_destroy_workqueue; + } + + error =register_virtio_driver (&virtio_blk); +if (error) + goto out_unregister_blkdev; +return 0 ; + +out_unregister_blkdev: +unregister_blkdev (major,"virtblk" ); +out_destroy_workqueue: +destroy_workqueue (virtblk_wq); +return error; } static void __exit fini(void) {unregister_blkdev (major,"virtblk" );unregister_virtio_driver (&virtio_blk); +destroy_workqueue (virtblk_wq); }module_init (init);module_exit (fini);
Rusty Russell
AppliedQuoted 10 lines · Christoph Hellwig
Wire up the
virtio_driverconfig_changedmethod 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_changedmethod 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 <[email protected]>
Thanks, applied!
Cheers,
Rusty.
Michael S. Tsirkin
maintainerQuoted 82 lines · Christoph Hellwig
Wire up the
virtio_driverconfig_changedmethod 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_changedmethod 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 <[email protected]>Index:
xfs/drivers/block/virtio_blk.c
===================================================================drivers/block/virtio_blk.c +45
-0@@ -6,10 +6,12 @@#include <linux/virtio.h> #include <linux/virtio_blk.h> #include <linux/scatterlist.h> +#include <linux/string_helpers.h> #define PART_BITS 4static int major, index; +struct workqueue_struct *virtblk_wq;struct virtio_blk {@@ -42,6 +44,11 @@ struct virtblk_requ8 status; }; +struct virtblk_config_change { +struct virtio_device *vdev; +struct work_struct work; +}; +static void blk_done (struct virtqueue *vq) {struct virtio_blk *vblk = vq->vdev->priv;@@ -291,6 +298,57 @@ static ssize_t virtblk_serial_show(struc }DEVICE_ATTR (serial,S_IRUGO , virtblk_serial_show,NULL ); +static void virtblk_config_changed_work (struct work_struct *work) +{ +struct virtblk_config_change *cfg = + container_of(work, struct virtblk_config_change, work); +struct virtio_device *vdev = cfg->vdev; +struct virtio_blk *vblk = vdev->priv; +struct request_queue *q = vblk->disk->queue; +char cap_str_2[10 ], cap_str_10[10 ]; +u64 capacity, size; + +/* Host must always specify the capacity. */ + vdev->config->get (vdev, offsetof(struct virtio_blk_config , capacity), + &capacity,sizeof (capacity)); + +/* If capacity is too big, truncate with warning. */ +if ((sector_t )capacity != capacity) { +dev_warn (&vdev->dev,"Capacity %llu too large: truncating\n" , + (unsigned long long )capacity); + capacity = (sector_t)-1 ; + } + + size = capacity *queue_logical_block_size (q); +string_get_size (size,STRING_UNITS_2 , cap_str_2,sizeof (cap_str_2)); +string_get_size (size,STRING_UNITS_10 , cap_str_10,sizeof (cap_str_10)); + +dev_notice (&vdev->dev, +"new size: %llu %d-byte logical blocks (%s/%s)\n" , + (unsigned long long )capacity, +queue_logical_block_size (q), + cap_str_10, cap_str_2); + +set_capacity (vblk->disk, capacity); + +} + +static void virtblk_config_changed (struct virtio_device *vdev) +{ +struct virtblk_config_change *cfg;
drivers/block/virtio_blk.c static ssize_t virtblk_serial_show(struc · line 348
Quoted 10 lines
drivers/block/virtio_blk.c +10
-0+ + cfg =kmalloc (sizeof (*cfg),GFP_ATOMIC ); +if (!cfg) { +dev_info (&vdev->dev,"skipping config change\n" ); +return ; + } + + cfg->vdev = vdev; +INIT_WORK (&cfg->work, virtblk_config_changed_work); +queue_work (virtblk_wq, &cfg->work);
This needs to be flushed on device removal, I think, otherwise the vdev pointer will go stale.
drivers/block/virtio_blk.c static ssize_t virtblk_serial_show(struc · line 325
Quoted 3 lines
drivers/block/virtio_blk.c +3
-0+} + +
Two empty lines :)
Quoted 67 lines
drivers/block/virtio_blk.c +30
-11static int __devinit virtblk_probe (struct virtio_device *vdev) {struct virtio_blk *vblk;@@ -508,27 +566,47 @@ static unsigned int features[] = {* Use __refdata to avoid this warning. */ static struct virtio_driver __refdata virtio_blk = { - .feature_table = features, - .feature_table_size =ARRAY_SIZE (features), - .driver.name =KBUILD_MODNAME , - .driver.owner =THIS_MODULE , - .id_table = id_table, - .probe = virtblk_probe, - .remove =__devexit_p (virtblk_remove), + .feature_table = features, + .feature_table_size =ARRAY_SIZE (features), + .driver.name =KBUILD_MODNAME , + .driver.owner =THIS_MODULE , + .id_table = id_table, + .probe = virtblk_probe, + .remove =__devexit_p (virtblk_remove), + .config_changed = virtblk_config_changed, };static int __init init (void ) { +int error; + + virtblk_wq =alloc_workqueue ("md_misc" ,0 ,0 ); +if (!virtblk_wq) +return -ENOMEM ; + major =register_blkdev (0 ,"virtblk" ); -if (major <0 ) -return major; -return register_virtio_driver (&virtio_blk); +if (major <0 ) { + error = major; + goto out_destroy_workqueue; + } + + error =register_virtio_driver (&virtio_blk); +if (error) + goto out_unregister_blkdev; +return 0 ; + +out_unregister_blkdev: +unregister_blkdev (major,"virtblk" ); +out_destroy_workqueue: +destroy_workqueue (virtblk_wq); +return error; } static void __exit fini(void) {unregister_blkdev (major,"virtblk" );unregister_virtio_driver (&virtio_blk); +destroy_workqueue (virtblk_wq); }module_init (init);module_exit (fini); --To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Quoted 2 lines · Michael S. Tsirkin
This needs to be flushed on device removal, I think, otherwise the vdev pointer will go stale.
Indeed.
Quoted 6 lines
drivers/block/virtio_blk.c static ssize_t virtblk_serial_show(struc · line 325
Quoted 4 lines
drivers/block/virtio_blk.c +3
-0+} + +Two empty lines :)
I'll fix it up.