diff options
-rw-r--r-- | fs/block_dev.c | 198 | ||||
-rw-r--r-- | include/linux/fs.h | 1 |
2 files changed, 175 insertions, 24 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index e59440c7e1cf..ea8385ea58ab 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -694,11 +694,144 @@ static bool bd_may_claim(struct block_device *bdev, struct block_device *whole, | |||
694 | } | 694 | } |
695 | 695 | ||
696 | /** | 696 | /** |
697 | * bd_prepare_to_claim - prepare to claim a block device | ||
698 | * @bdev: block device of interest | ||
699 | * @whole: the whole device containing @bdev, may equal @bdev | ||
700 | * @holder: holder trying to claim @bdev | ||
701 | * | ||
702 | * Prepare to claim @bdev. This function fails if @bdev is already | ||
703 | * claimed by another holder and waits if another claiming is in | ||
704 | * progress. This function doesn't actually claim. On successful | ||
705 | * return, the caller has ownership of bd_claiming and bd_holder[s]. | ||
706 | * | ||
707 | * CONTEXT: | ||
708 | * spin_lock(&bdev_lock). Might release bdev_lock, sleep and regrab | ||
709 | * it multiple times. | ||
710 | * | ||
711 | * RETURNS: | ||
712 | * 0 if @bdev can be claimed, -EBUSY otherwise. | ||
713 | */ | ||
714 | static int bd_prepare_to_claim(struct block_device *bdev, | ||
715 | struct block_device *whole, void *holder) | ||
716 | { | ||
717 | retry: | ||
718 | /* if someone else claimed, fail */ | ||
719 | if (!bd_may_claim(bdev, whole, holder)) | ||
720 | return -EBUSY; | ||
721 | |||
722 | /* if someone else is claiming, wait for it to finish */ | ||
723 | if (whole->bd_claiming && whole->bd_claiming != holder) { | ||
724 | wait_queue_head_t *wq = bit_waitqueue(&whole->bd_claiming, 0); | ||
725 | DEFINE_WAIT(wait); | ||
726 | |||
727 | prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); | ||
728 | spin_unlock(&bdev_lock); | ||
729 | schedule(); | ||
730 | finish_wait(wq, &wait); | ||
731 | spin_lock(&bdev_lock); | ||
732 | goto retry; | ||
733 | } | ||
734 | |||
735 | /* yay, all mine */ | ||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | /** | ||
740 | * bd_start_claiming - start claiming a block device | ||
741 | * @bdev: block device of interest | ||
742 | * @holder: holder trying to claim @bdev | ||
743 | * | ||
744 | * @bdev is about to be opened exclusively. Check @bdev can be opened | ||
745 | * exclusively and mark that an exclusive open is in progress. Each | ||
746 | * successful call to this function must be matched with a call to | ||
747 | * either bd_claim() or bd_abort_claiming(). If this function | ||
748 | * succeeds, the matching bd_claim() is guaranteed to succeed. | ||
749 | * | ||
750 | * CONTEXT: | ||
751 | * Might sleep. | ||
752 | * | ||
753 | * RETURNS: | ||
754 | * Pointer to the block device containing @bdev on success, ERR_PTR() | ||
755 | * value on failure. | ||
756 | */ | ||
757 | static struct block_device *bd_start_claiming(struct block_device *bdev, | ||
758 | void *holder) | ||
759 | { | ||
760 | struct gendisk *disk; | ||
761 | struct block_device *whole; | ||
762 | int partno, err; | ||
763 | |||
764 | might_sleep(); | ||
765 | |||
766 | /* | ||
767 | * @bdev might not have been initialized properly yet, look up | ||
768 | * and grab the outer block device the hard way. | ||
769 | */ | ||
770 | disk = get_gendisk(bdev->bd_dev, &partno); | ||
771 | if (!disk) | ||
772 | return ERR_PTR(-ENXIO); | ||
773 | |||
774 | whole = bdget_disk(disk, 0); | ||
775 | put_disk(disk); | ||
776 | if (!whole) | ||
777 | return ERR_PTR(-ENOMEM); | ||
778 | |||
779 | /* prepare to claim, if successful, mark claiming in progress */ | ||
780 | spin_lock(&bdev_lock); | ||
781 | |||
782 | err = bd_prepare_to_claim(bdev, whole, holder); | ||
783 | if (err == 0) { | ||
784 | whole->bd_claiming = holder; | ||
785 | spin_unlock(&bdev_lock); | ||
786 | return whole; | ||
787 | } else { | ||
788 | spin_unlock(&bdev_lock); | ||
789 | bdput(whole); | ||
790 | return ERR_PTR(err); | ||
791 | } | ||
792 | } | ||
793 | |||
794 | /* releases bdev_lock */ | ||
795 | static void __bd_abort_claiming(struct block_device *whole, void *holder) | ||
796 | { | ||
797 | BUG_ON(whole->bd_claiming != holder); | ||
798 | whole->bd_claiming = NULL; | ||
799 | wake_up_bit(&whole->bd_claiming, 0); | ||
800 | |||
801 | spin_unlock(&bdev_lock); | ||
802 | bdput(whole); | ||
803 | } | ||
804 | |||
805 | /** | ||
806 | * bd_abort_claiming - abort claiming a block device | ||
807 | * @whole: whole block device returned by bd_start_claiming() | ||
808 | * @holder: holder trying to claim @bdev | ||
809 | * | ||
810 | * Abort a claiming block started by bd_start_claiming(). Note that | ||
811 | * @whole is not the block device to be claimed but the whole device | ||
812 | * returned by bd_start_claiming(). | ||
813 | * | ||
814 | * CONTEXT: | ||
815 | * Grabs and releases bdev_lock. | ||
816 | */ | ||
817 | static void bd_abort_claiming(struct block_device *whole, void *holder) | ||
818 | { | ||
819 | spin_lock(&bdev_lock); | ||
820 | __bd_abort_claiming(whole, holder); /* releases bdev_lock */ | ||
821 | } | ||
822 | |||
823 | /** | ||
697 | * bd_claim - claim a block device | 824 | * bd_claim - claim a block device |
698 | * @bdev: block device to claim | 825 | * @bdev: block device to claim |
699 | * @holder: holder trying to claim @bdev | 826 | * @holder: holder trying to claim @bdev |
700 | * | 827 | * |
701 | * Try to claim @bdev. | 828 | * Try to claim @bdev which must have been opened successfully. This |
829 | * function may be called with or without preceding | ||
830 | * blk_start_claiming(). In the former case, this function is always | ||
831 | * successful and terminates the claiming block. | ||
832 | * | ||
833 | * CONTEXT: | ||
834 | * Might sleep. | ||
702 | * | 835 | * |
703 | * RETURNS: | 836 | * RETURNS: |
704 | * 0 if successful, -EBUSY if @bdev is already claimed. | 837 | * 0 if successful, -EBUSY if @bdev is already claimed. |
@@ -706,11 +839,14 @@ static bool bd_may_claim(struct block_device *bdev, struct block_device *whole, | |||
706 | int bd_claim(struct block_device *bdev, void *holder) | 839 | int bd_claim(struct block_device *bdev, void *holder) |
707 | { | 840 | { |
708 | struct block_device *whole = bdev->bd_contains; | 841 | struct block_device *whole = bdev->bd_contains; |
709 | int res = -EBUSY; | 842 | int res; |
843 | |||
844 | might_sleep(); | ||
710 | 845 | ||
711 | spin_lock(&bdev_lock); | 846 | spin_lock(&bdev_lock); |
712 | 847 | ||
713 | if (bd_may_claim(bdev, whole, holder)) { | 848 | res = bd_prepare_to_claim(bdev, whole, holder); |
849 | if (res == 0) { | ||
714 | /* note that for a whole device bd_holders | 850 | /* note that for a whole device bd_holders |
715 | * will be incremented twice, and bd_holder will | 851 | * will be incremented twice, and bd_holder will |
716 | * be set to bd_claim before being set to holder | 852 | * be set to bd_claim before being set to holder |
@@ -719,10 +855,13 @@ int bd_claim(struct block_device *bdev, void *holder) | |||
719 | whole->bd_holder = bd_claim; | 855 | whole->bd_holder = bd_claim; |
720 | bdev->bd_holders++; | 856 | bdev->bd_holders++; |
721 | bdev->bd_holder = holder; | 857 | bdev->bd_holder = holder; |
722 | res = 0; | ||
723 | } | 858 | } |
724 | 859 | ||
725 | spin_unlock(&bdev_lock); | 860 | if (whole->bd_claiming) |
861 | __bd_abort_claiming(whole, holder); /* releases bdev_lock */ | ||
862 | else | ||
863 | spin_unlock(&bdev_lock); | ||
864 | |||
726 | return res; | 865 | return res; |
727 | } | 866 | } |
728 | EXPORT_SYMBOL(bd_claim); | 867 | EXPORT_SYMBOL(bd_claim); |
@@ -1338,6 +1477,7 @@ EXPORT_SYMBOL(blkdev_get); | |||
1338 | 1477 | ||
1339 | static int blkdev_open(struct inode * inode, struct file * filp) | 1478 | static int blkdev_open(struct inode * inode, struct file * filp) |
1340 | { | 1479 | { |
1480 | struct block_device *whole = NULL; | ||
1341 | struct block_device *bdev; | 1481 | struct block_device *bdev; |
1342 | int res; | 1482 | int res; |
1343 | 1483 | ||
@@ -1360,22 +1500,25 @@ static int blkdev_open(struct inode * inode, struct file * filp) | |||
1360 | if (bdev == NULL) | 1500 | if (bdev == NULL) |
1361 | return -ENOMEM; | 1501 | return -ENOMEM; |
1362 | 1502 | ||
1503 | if (filp->f_mode & FMODE_EXCL) { | ||
1504 | whole = bd_start_claiming(bdev, filp); | ||
1505 | if (IS_ERR(whole)) { | ||
1506 | bdput(bdev); | ||
1507 | return PTR_ERR(whole); | ||
1508 | } | ||
1509 | } | ||
1510 | |||
1363 | filp->f_mapping = bdev->bd_inode->i_mapping; | 1511 | filp->f_mapping = bdev->bd_inode->i_mapping; |
1364 | 1512 | ||
1365 | res = blkdev_get(bdev, filp->f_mode); | 1513 | res = blkdev_get(bdev, filp->f_mode); |
1366 | if (res) | ||
1367 | return res; | ||
1368 | 1514 | ||
1369 | if (filp->f_mode & FMODE_EXCL) { | 1515 | if (whole) { |
1370 | res = bd_claim(bdev, filp); | 1516 | if (res == 0) |
1371 | if (res) | 1517 | BUG_ON(bd_claim(bdev, filp) != 0); |
1372 | goto out_blkdev_put; | 1518 | else |
1519 | bd_abort_claiming(whole, filp); | ||
1373 | } | 1520 | } |
1374 | 1521 | ||
1375 | return 0; | ||
1376 | |||
1377 | out_blkdev_put: | ||
1378 | blkdev_put(bdev, filp->f_mode); | ||
1379 | return res; | 1522 | return res; |
1380 | } | 1523 | } |
1381 | 1524 | ||
@@ -1586,27 +1729,34 @@ EXPORT_SYMBOL(lookup_bdev); | |||
1586 | */ | 1729 | */ |
1587 | struct block_device *open_bdev_exclusive(const char *path, fmode_t mode, void *holder) | 1730 | struct block_device *open_bdev_exclusive(const char *path, fmode_t mode, void *holder) |
1588 | { | 1731 | { |
1589 | struct block_device *bdev; | 1732 | struct block_device *bdev, *whole; |
1590 | int error = 0; | 1733 | int error; |
1591 | 1734 | ||
1592 | bdev = lookup_bdev(path); | 1735 | bdev = lookup_bdev(path); |
1593 | if (IS_ERR(bdev)) | 1736 | if (IS_ERR(bdev)) |
1594 | return bdev; | 1737 | return bdev; |
1595 | 1738 | ||
1739 | whole = bd_start_claiming(bdev, holder); | ||
1740 | if (IS_ERR(whole)) { | ||
1741 | bdput(bdev); | ||
1742 | return whole; | ||
1743 | } | ||
1744 | |||
1596 | error = blkdev_get(bdev, mode); | 1745 | error = blkdev_get(bdev, mode); |
1597 | if (error) | 1746 | if (error) |
1598 | return ERR_PTR(error); | 1747 | goto out_abort_claiming; |
1748 | |||
1599 | error = -EACCES; | 1749 | error = -EACCES; |
1600 | if ((mode & FMODE_WRITE) && bdev_read_only(bdev)) | 1750 | if ((mode & FMODE_WRITE) && bdev_read_only(bdev)) |
1601 | goto blkdev_put; | 1751 | goto out_blkdev_put; |
1602 | error = bd_claim(bdev, holder); | ||
1603 | if (error) | ||
1604 | goto blkdev_put; | ||
1605 | 1752 | ||
1753 | BUG_ON(bd_claim(bdev, holder) != 0); | ||
1606 | return bdev; | 1754 | return bdev; |
1607 | 1755 | ||
1608 | blkdev_put: | 1756 | out_blkdev_put: |
1609 | blkdev_put(bdev, mode); | 1757 | blkdev_put(bdev, mode); |
1758 | out_abort_claiming: | ||
1759 | bd_abort_claiming(whole, holder); | ||
1610 | return ERR_PTR(error); | 1760 | return ERR_PTR(error); |
1611 | } | 1761 | } |
1612 | 1762 | ||
diff --git a/include/linux/fs.h b/include/linux/fs.h index 39d57bc6cc71..31ee31be51e9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -651,6 +651,7 @@ struct block_device { | |||
651 | int bd_openers; | 651 | int bd_openers; |
652 | struct mutex bd_mutex; /* open/close mutex */ | 652 | struct mutex bd_mutex; /* open/close mutex */ |
653 | struct list_head bd_inodes; | 653 | struct list_head bd_inodes; |
654 | void * bd_claiming; | ||
654 | void * bd_holder; | 655 | void * bd_holder; |
655 | int bd_holders; | 656 | int bd_holders; |
656 | #ifdef CONFIG_SYSFS | 657 | #ifdef CONFIG_SYSFS |