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

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
...@@ -1078,72 +1078,6 @@ static struct gendisk *bdev_get_gendisk(struct block_device *bdev, int *partno) ...@@ -1078,72 +1078,6 @@ static struct gendisk *bdev_get_gendisk(struct block_device *bdev, int *partno)
return disk; 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) static void bd_clear_claiming(struct block_device *whole, void *holder)
{ {
lockdep_assert_held(&bdev_lock); lockdep_assert_held(&bdev_lock);
...@@ -1156,7 +1090,7 @@ static void bd_clear_claiming(struct block_device *whole, void *holder) ...@@ -1156,7 +1090,7 @@ static void bd_clear_claiming(struct block_device *whole, void *holder)
/** /**
* bd_finish_claiming - finish claiming of a block device * bd_finish_claiming - finish claiming of a block device
* @bdev: block device of interest * @bdev: block device of interest
* @whole: whole block device (returned from bd_start_claiming()) * @whole: whole block device
* @holder: holder that has claimed @bdev * @holder: holder that has claimed @bdev
* *
* Finish exclusive open of a block device. Mark the device as exlusively * 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, ...@@ -1182,7 +1116,7 @@ static void bd_finish_claiming(struct block_device *bdev,
/** /**
* bd_abort_claiming - abort claiming of a block device * bd_abort_claiming - abort claiming of a block device
* @bdev: block device of interest * @bdev: block device of interest
* @whole: whole block device (returned from bd_start_claiming()) * @whole: whole block device
* @holder: holder that has claimed @bdev * @holder: holder that has claimed @bdev
* *
* Abort claiming of a block device when the exclusive open failed. This can be * Abort claiming of a block device when the exclusive open failed. This can be
...@@ -1505,13 +1439,15 @@ EXPORT_SYMBOL_GPL(bdev_disk_changed); ...@@ -1505,13 +1439,15 @@ EXPORT_SYMBOL_GPL(bdev_disk_changed);
* mutex_lock_nested(whole->bd_mutex, 1) * 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; struct gendisk *disk;
int ret; int ret;
int partno; int partno;
int perm = 0; int perm = 0;
bool first_open = false, need_restart; bool first_open = false, unblock_events = true, need_restart;
if (mode & FMODE_READ) if (mode & FMODE_READ)
perm |= MAY_READ; perm |= MAY_READ;
...@@ -1533,6 +1469,25 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) ...@@ -1533,6 +1469,25 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
if (!disk) if (!disk)
goto out; 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); disk_block_events(disk);
mutex_lock_nested(&bdev->bd_mutex, for_part); mutex_lock_nested(&bdev->bd_mutex, for_part);
if (!bdev->bd_openers) { if (!bdev->bd_openers) {
...@@ -1576,18 +1531,11 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) ...@@ -1576,18 +1531,11 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
if (ret) if (ret)
goto out_clear; goto out_clear;
} else { } else {
struct block_device *whole;
whole = bdget_disk(disk, 0);
ret = -ENOMEM;
if (!whole)
goto out_clear;
BUG_ON(for_part); BUG_ON(for_part);
ret = __blkdev_get(whole, mode, 1); ret = __blkdev_get(whole, mode, NULL, 1);
if (ret) { if (ret)
bdput(whole);
goto out_clear; goto out_clear;
} bdev->bd_contains = bdgrab(whole);
bdev->bd_contains = whole;
bdev->bd_part = disk_get_part(disk, partno); bdev->bd_part = disk_get_part(disk, partno);
if (!(disk->flags & GENHD_FL_UP) || if (!(disk->flags & GENHD_FL_UP) ||
!bdev->bd_part || !bdev->bd_part->nr_sects) { !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) ...@@ -1616,11 +1564,30 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
bdev->bd_openers++; bdev->bd_openers++;
if (for_part) if (for_part)
bdev->bd_part_count++; 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); mutex_unlock(&bdev->bd_mutex);
if (unblock_events)
disk_unblock_events(disk); disk_unblock_events(disk);
/* only one opener holds refs to the module and disk */ /* only one opener holds refs to the module and disk */
if (!first_open) if (!first_open)
put_disk_and_module(disk); put_disk_and_module(disk);
if (whole)
bdput(whole);
return 0; return 0;
out_clear: out_clear:
...@@ -1631,13 +1598,18 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) ...@@ -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); __blkdev_put(bdev->bd_contains, mode, 1);
bdev->bd_contains = NULL; bdev->bd_contains = NULL;
out_unlock_bdev: out_unlock_bdev:
if (claiming)
bd_abort_claiming(bdev, claiming, holder);
mutex_unlock(&bdev->bd_mutex); mutex_unlock(&bdev->bd_mutex);
disk_unblock_events(disk); disk_unblock_events(disk);
out_put_whole:
if (whole)
bdput(whole);
out_put_disk:
put_disk_and_module(disk); put_disk_and_module(disk);
if (need_restart) if (need_restart)
goto restart; goto restart;
out: out:
return ret; return ret;
} }
...@@ -1662,50 +1634,11 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) ...@@ -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) int blkdev_get(struct block_device *bdev, fmode_t mode, void *holder)
{ {
struct block_device *whole = NULL;
int res; int res;
WARN_ON_ONCE((mode & FMODE_EXCL) && !holder); res =__blkdev_get(bdev, mode, holder, 0);
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);
}
if (res) if (res)
bdput(bdev); bdput(bdev);
return res; return res;
} }
EXPORT_SYMBOL(blkdev_get); EXPORT_SYMBOL(blkdev_get);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment