virtio: fix module/device unloading
Subsystems (5)
PATCH 1/1 virtio: fix module/device unloading
The virtio code never hooked through the ->remove callback. Although noone supports device removal at the moment, this code is already needed for module unloading.
This of course also revealed bugs in virtio_blk, virtio_net and lguest unloading paths.
Signed-off-by Rusty Russell <[email protected]>
drivers/block/virtio_blk.c +7 -3
@@ -223,7 +223,7 @@ static int virtblk_probe(struct virtio_d err =virtio_config_val (vdev,VIRTIO_CONFIG_BLK_F_CAPACITY , &cap);if (err) {dev_err (&vdev->dev,"Bad/missing capacity in config\n" ); - goto out_put_disk; + goto out_cleanup_queue; }/* If capacity is too big, truncate with warning. */
@@ -239,7 +239,7 @@ static int virtblk_probe(struct virtio_dblk_queue_max_segment_size (vblk->disk->queue, v); else if (err != -ENOENT) {dev_err (&vdev->dev,"Bad SIZE_MAX in config\n" ); - goto out_put_disk; + goto out_cleanup_queue; } err =virtio_config_val (vdev,VIRTIO_CONFIG_BLK_F_SEG_MAX , &v);
@@ -247,12 +247,14 @@ static int virtblk_probe(struct virtio_dblk_queue_max_hw_segments (vblk->disk->queue, v); else if (err != -ENOENT) {dev_err (&vdev->dev,"Bad SEG_MAX in config\n" ); - goto out_put_disk; + goto out_cleanup_queue; }add_disk (vblk->disk);return 0 ; +out_cleanup_queue: +blk_cleanup_queue (vblk->disk->queue); out_put_disk:put_disk (vblk->disk); out_unregister_blkdev:
@@ -277,6 +279,8 @@ static void virtblk_remove(struct virtioput_disk (vblk->disk);unregister_blkdev (major,"virtblk" );mempool_destroy (vblk->pool); +/* There should be nothing in the queue now, so no need to shutdown */ + vdev->config->del_vq (vblk->vq);kfree (vblk); }
drivers/lguest/lguest_device.c +2 -0
@@ -247,6 +247,8 @@ static void lg_del_vq(struct virtqueue * {struct lguest_vq_info *lvq = vq->priv; +/* Release the interrupt */ +free_irq (lvq->config.irq, vq);/* Tell virtio_ring.c to free the virtqueue. */ vring_del_virtqueue (vq);/* Unmap the pages containing the ring. */
drivers/net/virtio_net.c +6 -2
@@ -404,8 +404,12 @@ free:static void virtnet_remove (struct virtio_device *vdev) { -unregister_netdev (vdev->priv); -free_netdev (vdev->priv); +struct virtnet_info *vi = vdev->priv; + + vdev->config->del_vq (vi->svq); + vdev->config->del_vq (vi->rvq); +unregister_netdev (vi->dev); +free_netdev (vi->dev); }static struct virtio_device_id id_table[] = {
drivers/virtio/virtio.c +13 -0
@@ -96,10 +96,23 @@ static int virtio_dev_probe(struct devicreturn err; } +static int virtio_dev_remove (struct device *_d) +{ + struct virtio_device *dev = container_of(_d,struct virtio_device,dev); +struct virtio_driver *drv =container_of (dev->dev.driver, + struct virtio_driver, driver); + + dev->config->set_status (dev, dev->config->get_status (dev) + & ~VIRTIO_CONFIG_S_DRIVER ); + drv->remove (dev); +return 0 ; +} +int register_virtio_driver (struct virtio_driver *driver) { driver->driver.bus = &virtio_bus; driver->driver.probe = virtio_dev_probe; + driver->driver.remove = virtio_dev_remove;return driver_register (&driver->driver); }EXPORT_SYMBOL_GPL (register_virtio_driver);