Commit 5b642d8b authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe
Browse files

block: integrate bd_start_claiming into __blkdev_get



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: default avatarChristoph Hellwig <hch@lst.de>
Acked-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent ecbe6bc0
Loading
Loading
Loading
Loading
+55 −122
Original line number Diff line number Diff line
@@ -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);

	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);