aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/volumes.c
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.cz>2013-02-15 13:31:02 -0500
committerChris Mason <chris.mason@fusionio.com>2013-02-15 16:57:47 -0500
commit6f60cbd3ae442cb35861bb522f388db123d42ec1 (patch)
tree8cdca02d09d86f1e26b9a030ac39aa3675e2cd01 /fs/btrfs/volumes.c
parent2a745b14bc99d52c29d0c886a110321f651cf183 (diff)
btrfs: access superblock via pagecache in scan_one_device
btrfs_scan_one_device is calling set_blocksize() which can race with a concurrent process making dirty page cache pages. It can end up dropping dirty page cache pages on the floor, which isn't very nice when someone is just running btrfs dev scan to find filesystems on the box. Now that udev is registering btrfs devices as it discovers them, we can actually end up racing with our own mkfs program too. When this happens, we drop some of the important blocks written by mkfs. This commit changes scan_one_device to read the super out of the page cache instead of trying to use bread. This way we don't have to care about the blocksize of the device. This also drops the invalidate_bdev() call. It wasn't very polite to invalidate during the scan either. mkfs is putting the super into the page cache, there's no reason to invalidate at this point. Signed-off-by: David Sterba <dsterba@suse.cz> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs/btrfs/volumes.c')
-rw-r--r--fs/btrfs/volumes.c70
1 files changed, 64 insertions, 6 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 5cbb7f4b1672..5349e17d8863 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -792,26 +792,77 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
792 return ret; 792 return ret;
793} 793}
794 794
795/*
796 * Look for a btrfs signature on a device. This may be called out of the mount path
797 * and we are not allowed to call set_blocksize during the scan. The superblock
798 * is read via pagecache
799 */
795int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, 800int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
796 struct btrfs_fs_devices **fs_devices_ret) 801 struct btrfs_fs_devices **fs_devices_ret)
797{ 802{
798 struct btrfs_super_block *disk_super; 803 struct btrfs_super_block *disk_super;
799 struct block_device *bdev; 804 struct block_device *bdev;
800 struct buffer_head *bh; 805 struct page *page;
801 int ret; 806 void *p;
807 int ret = -EINVAL;
802 u64 devid; 808 u64 devid;
803 u64 transid; 809 u64 transid;
804 u64 total_devices; 810 u64 total_devices;
811 u64 bytenr;
812 pgoff_t index;
805 813
814 /*
815 * we would like to check all the supers, but that would make
816 * a btrfs mount succeed after a mkfs from a different FS.
817 * So, we need to add a special mount option to scan for
818 * later supers, using BTRFS_SUPER_MIRROR_MAX instead
819 */
820 bytenr = btrfs_sb_offset(0);
806 flags |= FMODE_EXCL; 821 flags |= FMODE_EXCL;
807 mutex_lock(&uuid_mutex); 822 mutex_lock(&uuid_mutex);
808 ret = btrfs_get_bdev_and_sb(path, flags, holder, 0, &bdev, &bh); 823
809 if (ret) 824 bdev = blkdev_get_by_path(path, flags, holder);
825
826 if (IS_ERR(bdev)) {
827 ret = PTR_ERR(bdev);
828 printk(KERN_INFO "btrfs: open %s failed\n", path);
810 goto error; 829 goto error;
811 disk_super = (struct btrfs_super_block *)bh->b_data; 830 }
831
832 /* make sure our super fits in the device */
833 if (bytenr + PAGE_CACHE_SIZE >= i_size_read(bdev->bd_inode))
834 goto error_bdev_put;
835
836 /* make sure our super fits in the page */
837 if (sizeof(*disk_super) > PAGE_CACHE_SIZE)
838 goto error_bdev_put;
839
840 /* make sure our super doesn't straddle pages on disk */
841 index = bytenr >> PAGE_CACHE_SHIFT;
842 if ((bytenr + sizeof(*disk_super) - 1) >> PAGE_CACHE_SHIFT != index)
843 goto error_bdev_put;
844
845 /* pull in the page with our super */
846 page = read_cache_page_gfp(bdev->bd_inode->i_mapping,
847 index, GFP_NOFS);
848
849 if (IS_ERR_OR_NULL(page))
850 goto error_bdev_put;
851
852 p = kmap(page);
853
854 /* align our pointer to the offset of the super block */
855 disk_super = p + (bytenr & ~PAGE_CACHE_MASK);
856
857 if (btrfs_super_bytenr(disk_super) != bytenr ||
858 strncmp((char *)(&disk_super->magic), BTRFS_MAGIC,
859 sizeof(disk_super->magic)))
860 goto error_unmap;
861
812 devid = btrfs_stack_device_id(&disk_super->dev_item); 862 devid = btrfs_stack_device_id(&disk_super->dev_item);
813 transid = btrfs_super_generation(disk_super); 863 transid = btrfs_super_generation(disk_super);
814 total_devices = btrfs_super_num_devices(disk_super); 864 total_devices = btrfs_super_num_devices(disk_super);
865
815 if (disk_super->label[0]) { 866 if (disk_super->label[0]) {
816 if (disk_super->label[BTRFS_LABEL_SIZE - 1]) 867 if (disk_super->label[BTRFS_LABEL_SIZE - 1])
817 disk_super->label[BTRFS_LABEL_SIZE - 1] = '\0'; 868 disk_super->label[BTRFS_LABEL_SIZE - 1] = '\0';
@@ -819,12 +870,19 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
819 } else { 870 } else {
820 printk(KERN_INFO "device fsid %pU ", disk_super->fsid); 871 printk(KERN_INFO "device fsid %pU ", disk_super->fsid);
821 } 872 }
873
822 printk(KERN_CONT "devid %llu transid %llu %s\n", 874 printk(KERN_CONT "devid %llu transid %llu %s\n",
823 (unsigned long long)devid, (unsigned long long)transid, path); 875 (unsigned long long)devid, (unsigned long long)transid, path);
876
824 ret = device_list_add(path, disk_super, devid, fs_devices_ret); 877 ret = device_list_add(path, disk_super, devid, fs_devices_ret);
825 if (!ret && fs_devices_ret) 878 if (!ret && fs_devices_ret)
826 (*fs_devices_ret)->total_devices = total_devices; 879 (*fs_devices_ret)->total_devices = total_devices;
827 brelse(bh); 880
881error_unmap:
882 kunmap(page);
883 page_cache_release(page);
884
885error_bdev_put:
828 blkdev_put(bdev, flags); 886 blkdev_put(bdev, flags);
829error: 887error:
830 mutex_unlock(&uuid_mutex); 888 mutex_unlock(&uuid_mutex);