Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 196819
b: refs/heads/master
c: 6b4517a
h: refs/heads/master
i:
  196817: aec44f7
  196815: 787accd
v: v3
  • Loading branch information
Tejun Heo authored and Jens Axboe committed Apr 27, 2010
1 parent 334fe27 commit 2a7bc06
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 25 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 1a3cbbc5a5e8a66934aa0947896a4aca6fd77298
refs/heads/master: 6b4517a7913a09d3259bb1d21c9cb300f12294bd
198 changes: 174 additions & 24 deletions trunk/fs/block_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,24 +693,160 @@ static bool bd_may_claim(struct block_device *bdev, struct block_device *whole,
return true; /* is a partition of an un-held device */
}

/**
* bd_prepare_to_claim - prepare to claim a block device
* @bdev: block device of interest
* @whole: the whole device containing @bdev, may equal @bdev
* @holder: holder trying to claim @bdev
*
* Prepare to claim @bdev. This function fails if @bdev is already
* claimed by another holder and waits if another claiming is in
* progress. This function doesn't actually claim. On successful
* return, the caller has ownership of bd_claiming and bd_holder[s].
*
* CONTEXT:
* spin_lock(&bdev_lock). Might release bdev_lock, sleep and regrab
* it multiple times.
*
* RETURNS:
* 0 if @bdev can be claimed, -EBUSY otherwise.
*/
static int bd_prepare_to_claim(struct block_device *bdev,
struct block_device *whole, void *holder)
{
retry:
/* if someone else claimed, fail */
if (!bd_may_claim(bdev, whole, holder))
return -EBUSY;

/* if someone else is claiming, wait for it to finish */
if (whole->bd_claiming && whole->bd_claiming != holder) {
wait_queue_head_t *wq = bit_waitqueue(&whole->bd_claiming, 0);
DEFINE_WAIT(wait);

prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE);
spin_unlock(&bdev_lock);
schedule();
finish_wait(wq, &wait);
spin_lock(&bdev_lock);
goto retry;
}

/* yay, all mine */
return 0;
}

/**
* 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_claim() or bd_abort_claiming(). If this function
* succeeds, the matching bd_claim() is guaranteed to succeed.
*
* 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 = get_gendisk(bdev->bd_dev, &partno);
if (!disk)
return ERR_PTR(-ENXIO);

whole = bdget_disk(disk, 0);
put_disk(disk);
if (!whole)
return ERR_PTR(-ENOMEM);

/* prepare to claim, if successful, mark claiming in progress */
spin_lock(&bdev_lock);

err = bd_prepare_to_claim(bdev, whole, holder);
if (err == 0) {
whole->bd_claiming = holder;
spin_unlock(&bdev_lock);
return whole;
} else {
spin_unlock(&bdev_lock);
bdput(whole);
return ERR_PTR(err);
}
}

/* releases bdev_lock */
static void __bd_abort_claiming(struct block_device *whole, void *holder)
{
BUG_ON(whole->bd_claiming != holder);
whole->bd_claiming = NULL;
wake_up_bit(&whole->bd_claiming, 0);

spin_unlock(&bdev_lock);
bdput(whole);
}

/**
* bd_abort_claiming - abort claiming a block device
* @whole: whole block device returned by bd_start_claiming()
* @holder: holder trying to claim @bdev
*
* Abort a claiming block started by bd_start_claiming(). Note that
* @whole is not the block device to be claimed but the whole device
* returned by bd_start_claiming().
*
* CONTEXT:
* Grabs and releases bdev_lock.
*/
static void bd_abort_claiming(struct block_device *whole, void *holder)
{
spin_lock(&bdev_lock);
__bd_abort_claiming(whole, holder); /* releases bdev_lock */
}

/**
* bd_claim - claim a block device
* @bdev: block device to claim
* @holder: holder trying to claim @bdev
*
* Try to claim @bdev.
* Try to claim @bdev which must have been opened successfully. This
* function may be called with or without preceding
* blk_start_claiming(). In the former case, this function is always
* successful and terminates the claiming block.
*
* CONTEXT:
* Might sleep.
*
* RETURNS:
* 0 if successful, -EBUSY if @bdev is already claimed.
*/
int bd_claim(struct block_device *bdev, void *holder)
{
struct block_device *whole = bdev->bd_contains;
int res = -EBUSY;
int res;

might_sleep();

spin_lock(&bdev_lock);

if (bd_may_claim(bdev, whole, holder)) {
res = bd_prepare_to_claim(bdev, whole, holder);
if (res == 0) {
/* note that for a whole device bd_holders
* will be incremented twice, and bd_holder will
* be set to bd_claim before being set to holder
Expand All @@ -719,10 +855,13 @@ int bd_claim(struct block_device *bdev, void *holder)
whole->bd_holder = bd_claim;
bdev->bd_holders++;
bdev->bd_holder = holder;
res = 0;
}

spin_unlock(&bdev_lock);
if (whole->bd_claiming)
__bd_abort_claiming(whole, holder); /* releases bdev_lock */
else
spin_unlock(&bdev_lock);

return res;
}
EXPORT_SYMBOL(bd_claim);
Expand Down Expand Up @@ -1338,6 +1477,7 @@ EXPORT_SYMBOL(blkdev_get);

static int blkdev_open(struct inode * inode, struct file * filp)
{
struct block_device *whole = NULL;
struct block_device *bdev;
int res;

Expand All @@ -1360,22 +1500,25 @@ static int blkdev_open(struct inode * inode, struct file * filp)
if (bdev == NULL)
return -ENOMEM;

if (filp->f_mode & FMODE_EXCL) {
whole = bd_start_claiming(bdev, filp);
if (IS_ERR(whole)) {
bdput(bdev);
return PTR_ERR(whole);
}
}

filp->f_mapping = bdev->bd_inode->i_mapping;

res = blkdev_get(bdev, filp->f_mode);
if (res)
return res;

if (filp->f_mode & FMODE_EXCL) {
res = bd_claim(bdev, filp);
if (res)
goto out_blkdev_put;
if (whole) {
if (res == 0)
BUG_ON(bd_claim(bdev, filp) != 0);
else
bd_abort_claiming(whole, filp);
}

return 0;

out_blkdev_put:
blkdev_put(bdev, filp->f_mode);
return res;
}

Expand Down Expand Up @@ -1586,27 +1729,34 @@ EXPORT_SYMBOL(lookup_bdev);
*/
struct block_device *open_bdev_exclusive(const char *path, fmode_t mode, void *holder)
{
struct block_device *bdev;
int error = 0;
struct block_device *bdev, *whole;
int error;

bdev = lookup_bdev(path);
if (IS_ERR(bdev))
return bdev;

whole = bd_start_claiming(bdev, holder);
if (IS_ERR(whole)) {
bdput(bdev);
return whole;
}

error = blkdev_get(bdev, mode);
if (error)
return ERR_PTR(error);
goto out_abort_claiming;

error = -EACCES;
if ((mode & FMODE_WRITE) && bdev_read_only(bdev))
goto blkdev_put;
error = bd_claim(bdev, holder);
if (error)
goto blkdev_put;
goto out_blkdev_put;

BUG_ON(bd_claim(bdev, holder) != 0);
return bdev;
blkdev_put:

out_blkdev_put:
blkdev_put(bdev, mode);
out_abort_claiming:
bd_abort_claiming(whole, holder);
return ERR_PTR(error);
}

Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ struct block_device {
int bd_openers;
struct mutex bd_mutex; /* open/close mutex */
struct list_head bd_inodes;
void * bd_claiming;
void * bd_holder;
int bd_holders;
#ifdef CONFIG_SYSFS
Expand Down

0 comments on commit 2a7bc06

Please sign in to comment.