Commit a0af469b authored by Chris Mason's avatar Chris Mason

Fix btrfs_open_devices to deal with changes since the scan ioctls

Devices can change after the scan ioctls are done, and btrfs_open_devices
needs to be able to verify them as they are opened and used by the FS.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent dfe25020
...@@ -1266,10 +1266,10 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1266,10 +1266,10 @@ struct btrfs_root *open_ctree(struct super_block *sb,
btrfs_parse_options(options, tree_root, NULL); btrfs_parse_options(options, tree_root, NULL);
if (btrfs_super_num_devices(disk_super) > fs_devices->num_devices) { if (btrfs_super_num_devices(disk_super) > fs_devices->open_devices) {
printk("Btrfs: wanted %llu devices, but found %llu\n", printk("Btrfs: wanted %llu devices, but found %llu\n",
(unsigned long long)btrfs_super_num_devices(disk_super), (unsigned long long)btrfs_super_num_devices(disk_super),
(unsigned long long)fs_devices->num_devices); (unsigned long long)fs_devices->open_devices);
if (btrfs_test_opt(tree_root, DEGRADED)) if (btrfs_test_opt(tree_root, DEGRADED))
printk("continuing in degraded mode\n"); printk("continuing in degraded mode\n");
else { else {
......
...@@ -71,6 +71,7 @@ int btrfs_cleanup_fs_uuids(void) ...@@ -71,6 +71,7 @@ int btrfs_cleanup_fs_uuids(void)
dev_list); dev_list);
if (dev->bdev) { if (dev->bdev) {
close_bdev_excl(dev->bdev); close_bdev_excl(dev->bdev);
fs_devices->open_devices--;
} }
list_del(&dev->dev_list); list_del(&dev->dev_list);
kfree(dev->name); kfree(dev->name);
...@@ -174,9 +175,10 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices) ...@@ -174,9 +175,10 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
list_for_each(cur, head) { list_for_each(cur, head) {
device = list_entry(cur, struct btrfs_device, dev_list); device = list_entry(cur, struct btrfs_device, dev_list);
if (!device->in_fs_metadata) { if (!device->in_fs_metadata) {
printk("getting rid of extra dev %s\n", device->name); if (device->bdev) {
if (device->bdev)
close_bdev_excl(device->bdev); close_bdev_excl(device->bdev);
fs_devices->open_devices--;
}
list_del(&device->dev_list); list_del(&device->dev_list);
list_del(&device->dev_alloc_list); list_del(&device->dev_alloc_list);
fs_devices->num_devices--; fs_devices->num_devices--;
...@@ -188,6 +190,7 @@ printk("getting rid of extra dev %s\n", device->name); ...@@ -188,6 +190,7 @@ printk("getting rid of extra dev %s\n", device->name);
mutex_unlock(&uuid_mutex); mutex_unlock(&uuid_mutex);
return 0; return 0;
} }
int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
{ {
struct list_head *head = &fs_devices->devices; struct list_head *head = &fs_devices->devices;
...@@ -199,10 +202,12 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) ...@@ -199,10 +202,12 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
device = list_entry(cur, struct btrfs_device, dev_list); device = list_entry(cur, struct btrfs_device, dev_list);
if (device->bdev) { if (device->bdev) {
close_bdev_excl(device->bdev); close_bdev_excl(device->bdev);
fs_devices->open_devices--;
} }
device->bdev = NULL; device->bdev = NULL;
device->in_fs_metadata = 0; device->in_fs_metadata = 0;
} }
fs_devices->mounted = 0;
mutex_unlock(&uuid_mutex); mutex_unlock(&uuid_mutex);
return 0; return 0;
} }
...@@ -214,9 +219,19 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, ...@@ -214,9 +219,19 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
struct list_head *head = &fs_devices->devices; struct list_head *head = &fs_devices->devices;
struct list_head *cur; struct list_head *cur;
struct btrfs_device *device; struct btrfs_device *device;
int ret; struct block_device *latest_bdev = NULL;
struct buffer_head *bh;
struct btrfs_super_block *disk_super;
u64 latest_devid = 0;
u64 latest_transid = 0;
u64 transid;
u64 devid;
int ret = 0;
mutex_lock(&uuid_mutex); mutex_lock(&uuid_mutex);
if (fs_devices->mounted)
goto out;
list_for_each(cur, head) { list_for_each(cur, head) {
device = list_entry(cur, struct btrfs_device, dev_list); device = list_entry(cur, struct btrfs_device, dev_list);
if (device->bdev) if (device->bdev)
...@@ -229,21 +244,52 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, ...@@ -229,21 +244,52 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
if (IS_ERR(bdev)) { if (IS_ERR(bdev)) {
printk("open %s failed\n", device->name); printk("open %s failed\n", device->name);
ret = PTR_ERR(bdev); goto error;
goto fail;
} }
set_blocksize(bdev, 4096); set_blocksize(bdev, 4096);
if (device->devid == fs_devices->latest_devid)
fs_devices->latest_bdev = bdev; bh = __bread(bdev, BTRFS_SUPER_INFO_OFFSET / 4096, 4096);
if (!bh)
goto error_close;
disk_super = (struct btrfs_super_block *)bh->b_data;
if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC,
sizeof(disk_super->magic)))
goto error_brelse;
devid = le64_to_cpu(disk_super->dev_item.devid);
if (devid != device->devid)
goto error_brelse;
transid = btrfs_super_generation(disk_super);
if (transid > latest_transid) {
latest_devid = devid;
latest_transid = transid;
latest_bdev = bdev;
}
device->bdev = bdev; device->bdev = bdev;
device->in_fs_metadata = 0; device->in_fs_metadata = 0;
fs_devices->open_devices++;
continue;
error_brelse:
brelse(bh);
error_close:
close_bdev_excl(bdev);
error:
continue;
} }
if (fs_devices->open_devices == 0) {
ret = -EIO;
goto out;
}
fs_devices->mounted = 1;
fs_devices->latest_bdev = latest_bdev;
fs_devices->latest_devid = latest_devid;
fs_devices->latest_trans = latest_transid;
out:
mutex_unlock(&uuid_mutex); mutex_unlock(&uuid_mutex);
return 0;
fail:
mutex_unlock(&uuid_mutex);
btrfs_close_devices(fs_devices);
return ret; return ret;
} }
...@@ -828,6 +874,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ...@@ -828,6 +874,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
if (device->bdev) { if (device->bdev) {
/* one close for the device struct or super_block */ /* one close for the device struct or super_block */
close_bdev_excl(device->bdev); close_bdev_excl(device->bdev);
root->fs_info->fs_devices->open_devices--;
} }
if (bdev) { if (bdev) {
/* one close for us */ /* one close for us */
...@@ -914,6 +961,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ...@@ -914,6 +961,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
list_add(&device->dev_alloc_list, list_add(&device->dev_alloc_list,
&root->fs_info->fs_devices->alloc_list); &root->fs_info->fs_devices->alloc_list);
root->fs_info->fs_devices->num_devices++; root->fs_info->fs_devices->num_devices++;
root->fs_info->fs_devices->open_devices++;
out: out:
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex); mutex_unlock(&root->fs_info->fs_mutex);
......
...@@ -71,16 +71,16 @@ struct btrfs_fs_devices { ...@@ -71,16 +71,16 @@ struct btrfs_fs_devices {
/* the device with this id has the most recent coyp of the super */ /* the device with this id has the most recent coyp of the super */
u64 latest_devid; u64 latest_devid;
u64 latest_trans; u64 latest_trans;
u64 lowest_devid;
u64 num_devices; u64 num_devices;
u64 open_devices;
struct block_device *latest_bdev; struct block_device *latest_bdev;
struct block_device *lowest_bdev;
/* all of the devices in the FS */ /* all of the devices in the FS */
struct list_head devices; struct list_head devices;
/* devices not currently being allocated */ /* devices not currently being allocated */
struct list_head alloc_list; struct list_head alloc_list;
struct list_head list; struct list_head list;
int mounted;
}; };
struct btrfs_bio_stripe { struct btrfs_bio_stripe {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment