|
@@ -772,79 +772,6 @@ static struct block_device *bd_start_claiming(struct block_device *bdev,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* 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 */
|
|
|
-}
|
|
|
-
|
|
|
-/* increment holders when we have a legitimate claim. requires bdev_lock */
|
|
|
-static void __bd_claim(struct block_device *bdev, struct block_device *whole,
|
|
|
- void *holder)
|
|
|
-{
|
|
|
- /* note that for a whole device bd_holders
|
|
|
- * will be incremented twice, and bd_holder will
|
|
|
- * be set to bd_may_claim before being set to holder
|
|
|
- */
|
|
|
- whole->bd_holders++;
|
|
|
- whole->bd_holder = bd_may_claim;
|
|
|
- bdev->bd_holders++;
|
|
|
- bdev->bd_holder = holder;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * bd_finish_claiming - finish claiming a block device
|
|
|
- * @bdev: block device of interest (passed to bd_start_claiming())
|
|
|
- * @whole: whole block device returned by bd_start_claiming()
|
|
|
- * @holder: holder trying to claim @bdev
|
|
|
- *
|
|
|
- * Finish a claiming block started by bd_start_claiming().
|
|
|
- *
|
|
|
- * CONTEXT:
|
|
|
- * Grabs and releases bdev_lock.
|
|
|
- */
|
|
|
-static void bd_finish_claiming(struct block_device *bdev,
|
|
|
- struct block_device *whole, void *holder)
|
|
|
-{
|
|
|
- spin_lock(&bdev_lock);
|
|
|
- BUG_ON(!bd_may_claim(bdev, whole, holder));
|
|
|
- __bd_claim(bdev, whole, holder);
|
|
|
- __bd_abort_claiming(whole, holder); /* not actually an abort */
|
|
|
-}
|
|
|
-
|
|
|
-static void bd_release(struct block_device *bdev)
|
|
|
-{
|
|
|
- spin_lock(&bdev_lock);
|
|
|
- if (!--bdev->bd_contains->bd_holders)
|
|
|
- bdev->bd_contains->bd_holder = NULL;
|
|
|
- if (!--bdev->bd_holders)
|
|
|
- bdev->bd_holder = NULL;
|
|
|
- spin_unlock(&bdev_lock);
|
|
|
-}
|
|
|
-
|
|
|
#ifdef CONFIG_SYSFS
|
|
|
static int add_symlink(struct kobject *from, struct kobject *to)
|
|
|
{
|
|
@@ -1223,10 +1150,30 @@ int blkdev_get(struct block_device *bdev, fmode_t mode, void *holder)
|
|
|
res = __blkdev_get(bdev, mode, 0);
|
|
|
|
|
|
if (whole) {
|
|
|
- if (res == 0)
|
|
|
- bd_finish_claiming(bdev, whole, holder);
|
|
|
- else
|
|
|
- bd_abort_claiming(whole, holder);
|
|
|
+ /* finish claiming */
|
|
|
+ spin_lock(&bdev_lock);
|
|
|
+
|
|
|
+ if (res == 0) {
|
|
|
+ BUG_ON(!bd_may_claim(bdev, whole, holder));
|
|
|
+ /*
|
|
|
+ * Note that for a whole device bd_holders
|
|
|
+ * will be incremented twice, and bd_holder
|
|
|
+ * will be set to bd_may_claim before being
|
|
|
+ * set to holder
|
|
|
+ */
|
|
|
+ whole->bd_holders++;
|
|
|
+ whole->bd_holder = bd_may_claim;
|
|
|
+ bdev->bd_holders++;
|
|
|
+ bdev->bd_holder = holder;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* tell others that we're done */
|
|
|
+ BUG_ON(whole->bd_claiming != holder);
|
|
|
+ whole->bd_claiming = NULL;
|
|
|
+ wake_up_bit(&whole->bd_claiming, 0);
|
|
|
+
|
|
|
+ spin_unlock(&bdev_lock);
|
|
|
+ bdput(whole);
|
|
|
}
|
|
|
|
|
|
return res;
|
|
@@ -1272,6 +1219,7 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
|
|
|
bdev->bd_part_count--;
|
|
|
|
|
|
if (!--bdev->bd_openers) {
|
|
|
+ WARN_ON_ONCE(bdev->bd_holders);
|
|
|
sync_blockdev(bdev);
|
|
|
kill_bdev(bdev);
|
|
|
}
|
|
@@ -1303,10 +1251,31 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
|
|
|
int blkdev_put(struct block_device *bdev, fmode_t mode)
|
|
|
{
|
|
|
if (mode & FMODE_EXCL) {
|
|
|
+ bool bdev_free;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Release a claim on the device. The holder fields
|
|
|
+ * are protected with bdev_lock. bd_mutex is to
|
|
|
+ * synchronize disk_holder unlinking.
|
|
|
+ */
|
|
|
mutex_lock(&bdev->bd_mutex);
|
|
|
- bd_release(bdev);
|
|
|
- if (!bdev->bd_holders)
|
|
|
+ spin_lock(&bdev_lock);
|
|
|
+
|
|
|
+ WARN_ON_ONCE(--bdev->bd_holders < 0);
|
|
|
+ WARN_ON_ONCE(--bdev->bd_contains->bd_holders < 0);
|
|
|
+
|
|
|
+ /* bd_contains might point to self, check in a separate step */
|
|
|
+ if ((bdev_free = !bdev->bd_holders))
|
|
|
+ bdev->bd_holder = NULL;
|
|
|
+ if (!bdev->bd_contains->bd_holders)
|
|
|
+ bdev->bd_contains->bd_holder = NULL;
|
|
|
+
|
|
|
+ spin_unlock(&bdev_lock);
|
|
|
+
|
|
|
+ /* if this was the last claim, holder link should go too */
|
|
|
+ if (bdev_free)
|
|
|
bd_unlink_disk_holder(bdev);
|
|
|
+
|
|
|
mutex_unlock(&bdev->bd_mutex);
|
|
|
}
|
|
|
return __blkdev_put(bdev, mode, 0);
|