]> www.infradead.org Git - users/hch/block.git/commitdiff
block: cleanup the bd_holder locking
authorChristoph Hellwig <hch@lst.de>
Tue, 18 Jan 2022 08:47:22 +0000 (09:47 +0100)
committerChristoph Hellwig <hch@lst.de>
Wed, 19 Jan 2022 15:19:55 +0000 (16:19 +0100)
Consistently use bdev_lock for all holder related fields and factor all
holder related work into helpers holding it instead of sometimes using
open_mutex.

Signed-off-by: Christoph Hellwig <hch@lst.de>
block/bdev.c

index 2e1d76ceabca77f5d4699ca33f0df39d5f5e56c2..a301a4264419c01167ec326ddb121d0bd5d396e7 100644 (file)
@@ -611,17 +611,16 @@ static void bd_clear_claiming(struct block_device *whole, void *holder)
        wake_up_bit(&whole->bd_claiming, 0);
 }
 
-/**
- * bd_finish_claiming - finish claiming of a block device
- * @bdev: block device of interest
- * @holder: holder that has claimed @bdev
- *
+/*
  * Finish exclusive open of a block device. Mark the device as exlusively
  * open by the holder and wake up all waiters for exclusive open to finish.
  */
-static void bd_finish_claiming(struct block_device *bdev, void *holder)
+static bool bd_finish_claiming(struct block_device *bdev, void *holder,
+               fmode_t mode)
 {
        struct block_device *whole = bdev_whole(bdev);
+       struct gendisk *disk = bdev->bd_disk;
+       bool unblock_events = false;
 
        spin_lock(&bdev_lock);
        BUG_ON(!bd_may_claim(bdev, whole, holder));
@@ -634,6 +633,42 @@ static void bd_finish_claiming(struct block_device *bdev, void *holder)
        bdev->bd_holders++;
        bdev->bd_holder = holder;
        bd_clear_claiming(whole, holder);
+
+       /*
+        * Block event polling for write claims if requested.  Any write holder
+        * makes the write_holder state stick until all are released.  This is
+        * good enough and tracking individual writeable reference is too
+        * fragile given the way @mode is used in blkdev_get/put().
+        */
+       if (!bdev->bd_write_holder && (mode & FMODE_WRITE) &&
+           (disk->event_flags & DISK_EVENT_FLAG_BLOCK_ON_EXCL_WRITE)) {
+               bdev->bd_write_holder = true;
+               unblock_events = true;
+       }
+
+       spin_unlock(&bdev_lock);
+       return unblock_events;
+}
+
+/*
+ * Release a claim on the device.
+ */
+static void bd_unclaim(struct block_device *bdev)
+{
+       struct block_device *whole = bdev_whole(bdev);
+
+       spin_lock(&bdev_lock);
+       WARN_ON_ONCE(bdev->bd_holders < 1);
+       WARN_ON_ONCE(whole->bd_holders < 1);
+       if (!--whole->bd_holders)
+               whole->bd_holder = NULL;
+       if (!--bdev->bd_holders) {
+               bdev->bd_holder = NULL;
+               if (bdev->bd_write_holder) {
+                       disk_unblock_events(bdev->bd_disk);
+                       bdev->bd_write_holder = false;
+               }
+       }
        spin_unlock(&bdev_lock);
 }
 
@@ -781,7 +816,6 @@ void blkdev_put_no_open(struct block_device *bdev)
  */
 struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
 {
-       bool unblock_events = true;
        struct block_device *bdev;
        struct gendisk *disk;
        int ret;
@@ -818,33 +852,17 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
                ret = blkdev_get_whole(bdev, mode);
        if (ret)
                goto put_module;
-       if (mode & FMODE_EXCL) {
-               bd_finish_claiming(bdev, holder);
-
-               /*
-                * Block event polling for write claims if requested.  Any write
-                * holder makes the write_holder state stick until all are
-                * released.  This is good enough and tracking individual
-                * writeable reference is too fragile given the way @mode is
-                * used in blkdev_get/put().
-                */
-               if ((mode & FMODE_WRITE) && !bdev->bd_write_holder &&
-                   (disk->event_flags & DISK_EVENT_FLAG_BLOCK_ON_EXCL_WRITE)) {
-                       bdev->bd_write_holder = true;
-                       unblock_events = false;
-               }
-       }
        mutex_unlock(&disk->open_mutex);
 
-       if (unblock_events)
+       if (!(mode & FMODE_EXCL) || bd_finish_claiming(bdev, holder, mode))
                disk_unblock_events(disk);
        return bdev;
 put_module:
        module_put(disk->fops->owner);
 abort_claiming:
+       mutex_unlock(&disk->open_mutex);
        if (mode & FMODE_EXCL)
                bd_abort_claiming(bdev, holder);
-       mutex_unlock(&disk->open_mutex);
        disk_unblock_events(disk);
 put_blkdev:
        blkdev_put_no_open(bdev);
@@ -904,43 +922,15 @@ void blkdev_put(struct block_device *bdev, fmode_t mode)
        if (bdev->bd_openers == 1)
                sync_blockdev(bdev);
 
-       mutex_lock(&disk->open_mutex);
-       if (mode & FMODE_EXCL) {
-               struct block_device *whole = bdev_whole(bdev);
-               bool bdev_free;
-
-               /*
-                * Release a claim on the device.  The holder fields
-                * are protected with bdev_lock.  open_mutex is to
-                * synchronize disk_holder unlinking.
-                */
-               spin_lock(&bdev_lock);
-
-               WARN_ON_ONCE(--bdev->bd_holders < 0);
-               WARN_ON_ONCE(--whole->bd_holders < 0);
-
-               if ((bdev_free = !bdev->bd_holders))
-                       bdev->bd_holder = NULL;
-               if (!whole->bd_holders)
-                       whole->bd_holder = NULL;
-
-               spin_unlock(&bdev_lock);
-
-               /*
-                * If this was the last claim, remove holder link and
-                * unblock evpoll if it was a write holder.
-                */
-               if (bdev_free && bdev->bd_write_holder) {
-                       disk_unblock_events(disk);
-                       bdev->bd_write_holder = false;
-               }
-       }
+       if (mode & FMODE_EXCL)
+               bd_unclaim(bdev);
 
        /*
         * Trigger event checking and tell drivers to flush MEDIA_CHANGE
         * event.  This is to ensure detection of media removal commanded
         * from userland - e.g. eject(1).
         */
+       mutex_lock(&disk->open_mutex);
        disk_flush_events(disk, DISK_EVENT_MEDIA_CHANGE);
 
        if (bdev_is_partition(bdev))