From: Christoph Hellwig Date: Tue, 18 Jan 2022 08:47:22 +0000 (+0100) Subject: block: cleanup the bd_holder locking X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=9cbaaa9a2298e5bc367379c47a561570c47a01eb;p=users%2Fhch%2Fblock.git block: cleanup the bd_holder locking Consistently use bdev_lock for all holder related fields and factor all holder related work into helpers holding it instead of sometimes using open_mutex. Signed-off-by: Christoph Hellwig --- diff --git a/block/bdev.c b/block/bdev.c index 2e1d76ceabca..a301a4264419 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -611,17 +611,16 @@ static void bd_clear_claiming(struct block_device *whole, void *holder) wake_up_bit(&whole->bd_claiming, 0); } -/** - * bd_finish_claiming - finish claiming of a block device - * @bdev: block device of interest - * @holder: holder that has claimed @bdev - * +/* * Finish exclusive open of a block device. Mark the device as exlusively * open by the holder and wake up all waiters for exclusive open to finish. */ -static void bd_finish_claiming(struct block_device *bdev, void *holder) +static bool bd_finish_claiming(struct block_device *bdev, void *holder, + fmode_t mode) { struct block_device *whole = bdev_whole(bdev); + struct gendisk *disk = bdev->bd_disk; + bool unblock_events = false; spin_lock(&bdev_lock); BUG_ON(!bd_may_claim(bdev, whole, holder)); @@ -634,6 +633,42 @@ static void bd_finish_claiming(struct block_device *bdev, void *holder) bdev->bd_holders++; bdev->bd_holder = holder; bd_clear_claiming(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 (!bdev->bd_write_holder && (mode & FMODE_WRITE) && + (disk->event_flags & DISK_EVENT_FLAG_BLOCK_ON_EXCL_WRITE)) { + bdev->bd_write_holder = true; + unblock_events = true; + } + + spin_unlock(&bdev_lock); + return unblock_events; +} + +/* + * Release a claim on the device. + */ +static void bd_unclaim(struct block_device *bdev) +{ + struct block_device *whole = bdev_whole(bdev); + + spin_lock(&bdev_lock); + WARN_ON_ONCE(bdev->bd_holders < 1); + WARN_ON_ONCE(whole->bd_holders < 1); + if (!--whole->bd_holders) + whole->bd_holder = NULL; + if (!--bdev->bd_holders) { + bdev->bd_holder = NULL; + if (bdev->bd_write_holder) { + disk_unblock_events(bdev->bd_disk); + bdev->bd_write_holder = false; + } + } spin_unlock(&bdev_lock); } @@ -781,7 +816,6 @@ void blkdev_put_no_open(struct block_device *bdev) */ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder) { - bool unblock_events = true; struct block_device *bdev; struct gendisk *disk; int ret; @@ -818,33 +852,17 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder) ret = blkdev_get_whole(bdev, mode); if (ret) goto put_module; - if (mode & FMODE_EXCL) { - bd_finish_claiming(bdev, 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 ((mode & FMODE_WRITE) && !bdev->bd_write_holder && - (disk->event_flags & DISK_EVENT_FLAG_BLOCK_ON_EXCL_WRITE)) { - bdev->bd_write_holder = true; - unblock_events = false; - } - } mutex_unlock(&disk->open_mutex); - if (unblock_events) + if (!(mode & FMODE_EXCL) || bd_finish_claiming(bdev, holder, mode)) disk_unblock_events(disk); return bdev; put_module: module_put(disk->fops->owner); abort_claiming: + mutex_unlock(&disk->open_mutex); if (mode & FMODE_EXCL) bd_abort_claiming(bdev, holder); - mutex_unlock(&disk->open_mutex); disk_unblock_events(disk); put_blkdev: blkdev_put_no_open(bdev); @@ -904,43 +922,15 @@ void blkdev_put(struct block_device *bdev, fmode_t mode) if (bdev->bd_openers == 1) sync_blockdev(bdev); - mutex_lock(&disk->open_mutex); - if (mode & FMODE_EXCL) { - struct block_device *whole = bdev_whole(bdev); - bool bdev_free; - - /* - * Release a claim on the device. The holder fields - * are protected with bdev_lock. open_mutex is to - * synchronize disk_holder unlinking. - */ - spin_lock(&bdev_lock); - - WARN_ON_ONCE(--bdev->bd_holders < 0); - WARN_ON_ONCE(--whole->bd_holders < 0); - - if ((bdev_free = !bdev->bd_holders)) - bdev->bd_holder = NULL; - if (!whole->bd_holders) - whole->bd_holder = NULL; - - spin_unlock(&bdev_lock); - - /* - * If this was the last claim, remove holder link and - * unblock evpoll if it was a write holder. - */ - if (bdev_free && bdev->bd_write_holder) { - disk_unblock_events(disk); - bdev->bd_write_holder = false; - } - } + if (mode & FMODE_EXCL) + bd_unclaim(bdev); /* * Trigger event checking and tell drivers to flush MEDIA_CHANGE * event. This is to ensure detection of media removal commanded * from userland - e.g. eject(1). */ + mutex_lock(&disk->open_mutex); disk_flush_events(disk, DISK_EVENT_MEDIA_CHANGE); if (bdev_is_partition(bdev))