OSDN Git Service

block: integrate bd_start_claiming into __blkdev_get
authorChristoph Hellwig <hch@lst.de>
Thu, 16 Jul 2020 14:33:10 +0000 (16:33 +0200)
committerJens Axboe <axboe@kernel.dk>
Thu, 16 Jul 2020 15:35:44 +0000 (09:35 -0600)
bd_start_claiming duplicates a lot of the work done in __blkdev_get.
Integrate the two functions to avoid the duplicate work, and to do the
right thing for the md -ERESTARTSYS corner case.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/block_dev.c

index ee80bd8..3f94a06 100644 (file)
@@ -1078,72 +1078,6 @@ static struct gendisk *bdev_get_gendisk(struct block_device *bdev, int *partno)
        return disk;
 }
 
-/**
- * bd_start_claiming - start claiming a block device
- * @bdev: block device of interest
- * @holder: holder trying to claim @bdev
- *
- * @bdev is about to be opened exclusively.  Check @bdev can be opened
- * exclusively and mark that an exclusive open is in progress.  Each
- * successful call to this function must be matched with a call to
- * either bd_finish_claiming() or bd_abort_claiming() (which do not
- * fail).
- *
- * This function is used to gain exclusive access to the block device
- * without actually causing other exclusive open attempts to fail. It
- * should be used when the open sequence itself requires exclusive
- * access but may subsequently fail.
- *
- * CONTEXT:
- * Might sleep.
- *
- * RETURNS:
- * Pointer to the block device containing @bdev on success, ERR_PTR()
- * value on failure.
- */
-static struct block_device *bd_start_claiming(struct block_device *bdev,
-               void *holder)
-{
-       struct gendisk *disk;
-       struct block_device *whole;
-       int partno, err;
-
-       might_sleep();
-
-       /*
-        * @bdev might not have been initialized properly yet, look up
-        * and grab the outer block device the hard way.
-        */
-       disk = bdev_get_gendisk(bdev, &partno);
-       if (!disk)
-               return ERR_PTR(-ENXIO);
-
-       /*
-        * Normally, @bdev should equal what's returned from bdget_disk()
-        * if partno is 0; however, some drivers (floppy) use multiple
-        * bdev's for the same physical device and @bdev may be one of the
-        * aliases.  Keep @bdev if partno is 0.  This means claimer
-        * tracking is broken for those devices but it has always been that
-        * way.
-        */
-       if (partno)
-               whole = bdget_disk(disk, 0);
-       else
-               whole = bdgrab(bdev);
-
-       put_disk_and_module(disk);
-       if (!whole)
-               return ERR_PTR(-ENOMEM);
-
-       err = bd_prepare_to_claim(bdev, whole, holder);
-       if (err) {
-               bdput(whole);
-               return ERR_PTR(err);
-       }
-
-       return whole;
-}
-
 static void bd_clear_claiming(struct block_device *whole, void *holder)
 {
        lockdep_assert_held(&bdev_lock);
@@ -1156,7 +1090,7 @@ static void bd_clear_claiming(struct block_device *whole, void *holder)
 /**
  * bd_finish_claiming - finish claiming of a block device
  * @bdev: block device of interest
- * @whole: whole block device (returned from bd_start_claiming())
+ * @whole: whole block device
  * @holder: holder that has claimed @bdev
  *
  * Finish exclusive open of a block device. Mark the device as exlusively
@@ -1182,7 +1116,7 @@ static void bd_finish_claiming(struct block_device *bdev,
 /**
  * bd_abort_claiming - abort claiming of a block device
  * @bdev: block device of interest
- * @whole: whole block device (returned from bd_start_claiming())
+ * @whole: whole block device
  * @holder: holder that has claimed @bdev
  *
  * Abort claiming of a block device when the exclusive open failed. This can be
@@ -1505,13 +1439,15 @@ EXPORT_SYMBOL_GPL(bdev_disk_changed);
  *    mutex_lock_nested(whole->bd_mutex, 1)
  */
 
-static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
+static int __blkdev_get(struct block_device *bdev, fmode_t mode, void *holder,
+               int for_part)
 {
+       struct block_device *whole = NULL, *claiming = NULL;
        struct gendisk *disk;
        int ret;
        int partno;
        int perm = 0;
-       bool first_open = false, need_restart;
+       bool first_open = false, unblock_events = true, need_restart;
 
        if (mode & FMODE_READ)
                perm |= MAY_READ;
@@ -1533,6 +1469,25 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
        if (!disk)
                goto out;
 
+       if (partno) {
+               whole = bdget_disk(disk, 0);
+               if (!whole) {
+                       ret = -ENOMEM;
+                       goto out_put_disk;
+               }
+       }
+
+       if (!for_part && (mode & FMODE_EXCL)) {
+               WARN_ON_ONCE(!holder);
+               if (whole)
+                       claiming = whole;
+               else
+                       claiming = bdev;
+               ret = bd_prepare_to_claim(bdev, claiming, holder);
+               if (ret)
+                       goto out_put_whole;
+       }
+
        disk_block_events(disk);
        mutex_lock_nested(&bdev->bd_mutex, for_part);
        if (!bdev->bd_openers) {
@@ -1576,18 +1531,11 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
                        if (ret)
                                goto out_clear;
                } else {
-                       struct block_device *whole;
-                       whole = bdget_disk(disk, 0);
-                       ret = -ENOMEM;
-                       if (!whole)
-                               goto out_clear;
                        BUG_ON(for_part);
-                       ret = __blkdev_get(whole, mode, 1);
-                       if (ret) {
-                               bdput(whole);
+                       ret = __blkdev_get(whole, mode, NULL, 1);
+                       if (ret)
                                goto out_clear;
-                       }
-                       bdev->bd_contains = whole;
+                       bdev->bd_contains = bdgrab(whole);
                        bdev->bd_part = disk_get_part(disk, partno);
                        if (!(disk->flags & GENHD_FL_UP) ||
                            !bdev->bd_part || !bdev->bd_part->nr_sects) {
@@ -1616,11 +1564,30 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
        bdev->bd_openers++;
        if (for_part)
                bdev->bd_part_count++;
+       if (claiming)
+               bd_finish_claiming(bdev, claiming, 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 (claiming && (mode & FMODE_WRITE) && !bdev->bd_write_holder &&
+           (disk->flags & GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE)) {
+               bdev->bd_write_holder = true;
+               unblock_events = false;
+       }
        mutex_unlock(&bdev->bd_mutex);
-       disk_unblock_events(disk);
+
+       if (unblock_events)
+               disk_unblock_events(disk);
+
        /* only one opener holds refs to the module and disk */
        if (!first_open)
                put_disk_and_module(disk);
+       if (whole)
+               bdput(whole);
        return 0;
 
  out_clear:
@@ -1631,13 +1598,18 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
                __blkdev_put(bdev->bd_contains, mode, 1);
        bdev->bd_contains = NULL;
  out_unlock_bdev:
+       if (claiming)
+               bd_abort_claiming(bdev, claiming, holder);
        mutex_unlock(&bdev->bd_mutex);
        disk_unblock_events(disk);
+ out_put_whole:
+       if (whole)
+               bdput(whole);
+ out_put_disk:
        put_disk_and_module(disk);
        if (need_restart)
                goto restart;
  out:
-
        return ret;
 }
 
@@ -1662,50 +1634,11 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
  */
 int blkdev_get(struct block_device *bdev, fmode_t mode, void *holder)
 {
-       struct block_device *whole = NULL;
        int res;
 
-       WARN_ON_ONCE((mode & FMODE_EXCL) && !holder);
-
-       if ((mode & FMODE_EXCL) && holder) {
-               whole = bd_start_claiming(bdev, holder);
-               if (IS_ERR(whole)) {
-                       bdput(bdev);
-                       return PTR_ERR(whole);
-               }
-       }
-
-       res = __blkdev_get(bdev, mode, 0);
-
-       if (whole) {
-               struct gendisk *disk = whole->bd_disk;
-
-               /* finish claiming */
-               mutex_lock(&bdev->bd_mutex);
-               if (!res)
-                       bd_finish_claiming(bdev, whole, holder);
-               else
-                       bd_abort_claiming(bdev, 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 (!res && (mode & FMODE_WRITE) && !bdev->bd_write_holder &&
-                   (disk->flags & GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE)) {
-                       bdev->bd_write_holder = true;
-                       disk_block_events(disk);
-               }
-
-               mutex_unlock(&bdev->bd_mutex);
-               bdput(whole);
-       }
-
+       res =__blkdev_get(bdev, mode, holder, 0);
        if (res)
                bdput(bdev);
-
        return res;
 }
 EXPORT_SYMBOL(blkdev_get);