@@ -33,6 +33,15 @@ struct virtio_blk_vq {
3333} ____cacheline_aligned_in_smp ;
3434
3535struct virtio_blk {
36+ /*
37+ * This mutex must be held by anything that may run after
38+ * virtblk_remove() sets vblk->vdev to NULL.
39+ *
40+ * blk-mq, virtqueue processing, and sysfs attribute code paths are
41+ * shut down before vblk->vdev is set to NULL and therefore do not need
42+ * to hold this mutex.
43+ */
44+ struct mutex vdev_mutex ;
3645 struct virtio_device * vdev ;
3746
3847 /* The disk structure for the kernel. */
@@ -44,6 +53,13 @@ struct virtio_blk {
4453 /* Process context for config space updates */
4554 struct work_struct config_work ;
4655
56+ /*
57+ * Tracks references from block_device_operations open/release and
58+ * virtio_driver probe/remove so this object can be freed once no
59+ * longer in use.
60+ */
61+ refcount_t refs ;
62+
4763 /* What host tells us, plus 2 for header & tailer. */
4864 unsigned int sg_elems ;
4965
@@ -295,10 +311,55 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
295311 return err ;
296312}
297313
314+ static void virtblk_get (struct virtio_blk * vblk )
315+ {
316+ refcount_inc (& vblk -> refs );
317+ }
318+
319+ static void virtblk_put (struct virtio_blk * vblk )
320+ {
321+ if (refcount_dec_and_test (& vblk -> refs )) {
322+ ida_simple_remove (& vd_index_ida , vblk -> index );
323+ mutex_destroy (& vblk -> vdev_mutex );
324+ kfree (vblk );
325+ }
326+ }
327+
328+ static int virtblk_open (struct block_device * bd , fmode_t mode )
329+ {
330+ struct virtio_blk * vblk = bd -> bd_disk -> private_data ;
331+ int ret = 0 ;
332+
333+ mutex_lock (& vblk -> vdev_mutex );
334+
335+ if (vblk -> vdev )
336+ virtblk_get (vblk );
337+ else
338+ ret = - ENXIO ;
339+
340+ mutex_unlock (& vblk -> vdev_mutex );
341+ return ret ;
342+ }
343+
344+ static void virtblk_release (struct gendisk * disk , fmode_t mode )
345+ {
346+ struct virtio_blk * vblk = disk -> private_data ;
347+
348+ virtblk_put (vblk );
349+ }
350+
298351/* We provide getgeo only to please some old bootloader/partitioning tools */
299352static int virtblk_getgeo (struct block_device * bd , struct hd_geometry * geo )
300353{
301354 struct virtio_blk * vblk = bd -> bd_disk -> private_data ;
355+ int ret = 0 ;
356+
357+ mutex_lock (& vblk -> vdev_mutex );
358+
359+ if (!vblk -> vdev ) {
360+ ret = - ENXIO ;
361+ goto out ;
362+ }
302363
303364 /* see if the host passed in geometry config */
304365 if (virtio_has_feature (vblk -> vdev , VIRTIO_BLK_F_GEOMETRY )) {
@@ -314,11 +375,15 @@ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
314375 geo -> sectors = 1 << 5 ;
315376 geo -> cylinders = get_capacity (bd -> bd_disk ) >> 11 ;
316377 }
317- return 0 ;
378+ out :
379+ mutex_unlock (& vblk -> vdev_mutex );
380+ return ret ;
318381}
319382
320383static const struct block_device_operations virtblk_fops = {
321384 .owner = THIS_MODULE ,
385+ .open = virtblk_open ,
386+ .release = virtblk_release ,
322387 .getgeo = virtblk_getgeo ,
323388};
324389
@@ -655,6 +720,10 @@ static int virtblk_probe(struct virtio_device *vdev)
655720 goto out_free_index ;
656721 }
657722
723+ /* This reference is dropped in virtblk_remove(). */
724+ refcount_set (& vblk -> refs , 1 );
725+ mutex_init (& vblk -> vdev_mutex );
726+
658727 vblk -> vdev = vdev ;
659728 vblk -> sg_elems = sg_elems ;
660729
@@ -820,8 +889,6 @@ static int virtblk_probe(struct virtio_device *vdev)
820889static void virtblk_remove (struct virtio_device * vdev )
821890{
822891 struct virtio_blk * vblk = vdev -> priv ;
823- int index = vblk -> index ;
824- int refc ;
825892
826893 /* Make sure no work handler is accessing the device. */
827894 flush_work (& vblk -> config_work );
@@ -831,18 +898,21 @@ static void virtblk_remove(struct virtio_device *vdev)
831898
832899 blk_mq_free_tag_set (& vblk -> tag_set );
833900
901+ mutex_lock (& vblk -> vdev_mutex );
902+
834903 /* Stop all the virtqueues. */
835904 vdev -> config -> reset (vdev );
836905
837- refc = kref_read (& disk_to_dev (vblk -> disk )-> kobj .kref );
906+ /* Virtqueues are stopped, nothing can use vblk->vdev anymore. */
907+ vblk -> vdev = NULL ;
908+
838909 put_disk (vblk -> disk );
839910 vdev -> config -> del_vqs (vdev );
840911 kfree (vblk -> vqs );
841- kfree (vblk );
842912
843- /* Only free device id if we don't have any users */
844- if ( refc == 1 )
845- ida_simple_remove ( & vd_index_ida , index );
913+ mutex_unlock ( & vblk -> vdev_mutex );
914+
915+ virtblk_put ( vblk );
846916}
847917
848918#ifdef CONFIG_PM_SLEEP
0 commit comments