|
@@ -118,6 +118,21 @@ const u64 btrfs_raid_group[BTRFS_NR_RAID_TYPES] = {
|
|
[BTRFS_RAID_RAID6] = BTRFS_BLOCK_GROUP_RAID6,
|
|
[BTRFS_RAID_RAID6] = BTRFS_BLOCK_GROUP_RAID6,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Table to convert BTRFS_RAID_* to the error code if minimum number of devices
|
|
|
|
+ * condition is not met. Zero means there's no corresponding
|
|
|
|
+ * BTRFS_ERROR_DEV_*_NOT_MET value.
|
|
|
|
+ */
|
|
|
|
+const int btrfs_raid_mindev_error[BTRFS_NR_RAID_TYPES] = {
|
|
|
|
+ [BTRFS_RAID_RAID10] = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET,
|
|
|
|
+ [BTRFS_RAID_RAID1] = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET,
|
|
|
|
+ [BTRFS_RAID_DUP] = 0,
|
|
|
|
+ [BTRFS_RAID_RAID0] = 0,
|
|
|
|
+ [BTRFS_RAID_SINGLE] = 0,
|
|
|
|
+ [BTRFS_RAID_RAID5] = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET,
|
|
|
|
+ [BTRFS_RAID_RAID6] = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET,
|
|
|
|
+};
|
|
|
|
+
|
|
static int init_first_rw_device(struct btrfs_trans_handle *trans,
|
|
static int init_first_rw_device(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root,
|
|
struct btrfs_root *root,
|
|
struct btrfs_device *device);
|
|
struct btrfs_device *device);
|
|
@@ -699,7 +714,8 @@ static noinline int device_list_add(const char *path,
|
|
* if there is new btrfs on an already registered device,
|
|
* if there is new btrfs on an already registered device,
|
|
* then remove the stale device entry.
|
|
* then remove the stale device entry.
|
|
*/
|
|
*/
|
|
- btrfs_free_stale_device(device);
|
|
|
|
|
|
+ if (ret > 0)
|
|
|
|
+ btrfs_free_stale_device(device);
|
|
|
|
|
|
*fs_devices_ret = fs_devices;
|
|
*fs_devices_ret = fs_devices;
|
|
|
|
|
|
@@ -988,6 +1004,56 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void btrfs_release_disk_super(struct page *page)
|
|
|
|
+{
|
|
|
|
+ kunmap(page);
|
|
|
|
+ put_page(page);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int btrfs_read_disk_super(struct block_device *bdev, u64 bytenr,
|
|
|
|
+ struct page **page, struct btrfs_super_block **disk_super)
|
|
|
|
+{
|
|
|
|
+ void *p;
|
|
|
|
+ pgoff_t index;
|
|
|
|
+
|
|
|
|
+ /* make sure our super fits in the device */
|
|
|
|
+ if (bytenr + PAGE_SIZE >= i_size_read(bdev->bd_inode))
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ /* make sure our super fits in the page */
|
|
|
|
+ if (sizeof(**disk_super) > PAGE_SIZE)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ /* make sure our super doesn't straddle pages on disk */
|
|
|
|
+ index = bytenr >> PAGE_SHIFT;
|
|
|
|
+ if ((bytenr + sizeof(**disk_super) - 1) >> PAGE_SHIFT != index)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ /* pull in the page with our super */
|
|
|
|
+ *page = read_cache_page_gfp(bdev->bd_inode->i_mapping,
|
|
|
|
+ index, GFP_KERNEL);
|
|
|
|
+
|
|
|
|
+ if (IS_ERR_OR_NULL(*page))
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ p = kmap(*page);
|
|
|
|
+
|
|
|
|
+ /* align our pointer to the offset of the super block */
|
|
|
|
+ *disk_super = p + (bytenr & ~PAGE_MASK);
|
|
|
|
+
|
|
|
|
+ if (btrfs_super_bytenr(*disk_super) != bytenr ||
|
|
|
|
+ btrfs_super_magic(*disk_super) != BTRFS_MAGIC) {
|
|
|
|
+ btrfs_release_disk_super(*page);
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((*disk_super)->label[0] &&
|
|
|
|
+ (*disk_super)->label[BTRFS_LABEL_SIZE - 1])
|
|
|
|
+ (*disk_super)->label[BTRFS_LABEL_SIZE - 1] = '\0';
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Look for a btrfs signature on a device. This may be called out of the mount path
|
|
* Look for a btrfs signature on a device. This may be called out of the mount path
|
|
* and we are not allowed to call set_blocksize during the scan. The superblock
|
|
* and we are not allowed to call set_blocksize during the scan. The superblock
|
|
@@ -999,13 +1065,11 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
|
struct btrfs_super_block *disk_super;
|
|
struct btrfs_super_block *disk_super;
|
|
struct block_device *bdev;
|
|
struct block_device *bdev;
|
|
struct page *page;
|
|
struct page *page;
|
|
- void *p;
|
|
|
|
int ret = -EINVAL;
|
|
int ret = -EINVAL;
|
|
u64 devid;
|
|
u64 devid;
|
|
u64 transid;
|
|
u64 transid;
|
|
u64 total_devices;
|
|
u64 total_devices;
|
|
u64 bytenr;
|
|
u64 bytenr;
|
|
- pgoff_t index;
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
* we would like to check all the supers, but that would make
|
|
* we would like to check all the supers, but that would make
|
|
@@ -1018,41 +1082,14 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
|
mutex_lock(&uuid_mutex);
|
|
mutex_lock(&uuid_mutex);
|
|
|
|
|
|
bdev = blkdev_get_by_path(path, flags, holder);
|
|
bdev = blkdev_get_by_path(path, flags, holder);
|
|
-
|
|
|
|
if (IS_ERR(bdev)) {
|
|
if (IS_ERR(bdev)) {
|
|
ret = PTR_ERR(bdev);
|
|
ret = PTR_ERR(bdev);
|
|
goto error;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
|
|
- /* make sure our super fits in the device */
|
|
|
|
- if (bytenr + PAGE_SIZE >= i_size_read(bdev->bd_inode))
|
|
|
|
- goto error_bdev_put;
|
|
|
|
-
|
|
|
|
- /* make sure our super fits in the page */
|
|
|
|
- if (sizeof(*disk_super) > PAGE_SIZE)
|
|
|
|
|
|
+ if (btrfs_read_disk_super(bdev, bytenr, &page, &disk_super))
|
|
goto error_bdev_put;
|
|
goto error_bdev_put;
|
|
|
|
|
|
- /* make sure our super doesn't straddle pages on disk */
|
|
|
|
- index = bytenr >> PAGE_SHIFT;
|
|
|
|
- if ((bytenr + sizeof(*disk_super) - 1) >> PAGE_SHIFT != index)
|
|
|
|
- goto error_bdev_put;
|
|
|
|
-
|
|
|
|
- /* pull in the page with our super */
|
|
|
|
- page = read_cache_page_gfp(bdev->bd_inode->i_mapping,
|
|
|
|
- index, GFP_NOFS);
|
|
|
|
-
|
|
|
|
- if (IS_ERR_OR_NULL(page))
|
|
|
|
- goto error_bdev_put;
|
|
|
|
-
|
|
|
|
- p = kmap(page);
|
|
|
|
-
|
|
|
|
- /* align our pointer to the offset of the super block */
|
|
|
|
- disk_super = p + (bytenr & ~PAGE_MASK);
|
|
|
|
-
|
|
|
|
- if (btrfs_super_bytenr(disk_super) != bytenr ||
|
|
|
|
- btrfs_super_magic(disk_super) != BTRFS_MAGIC)
|
|
|
|
- goto error_unmap;
|
|
|
|
-
|
|
|
|
devid = btrfs_stack_device_id(&disk_super->dev_item);
|
|
devid = btrfs_stack_device_id(&disk_super->dev_item);
|
|
transid = btrfs_super_generation(disk_super);
|
|
transid = btrfs_super_generation(disk_super);
|
|
total_devices = btrfs_super_num_devices(disk_super);
|
|
total_devices = btrfs_super_num_devices(disk_super);
|
|
@@ -1060,8 +1097,6 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
|
ret = device_list_add(path, disk_super, devid, fs_devices_ret);
|
|
ret = device_list_add(path, disk_super, devid, fs_devices_ret);
|
|
if (ret > 0) {
|
|
if (ret > 0) {
|
|
if (disk_super->label[0]) {
|
|
if (disk_super->label[0]) {
|
|
- if (disk_super->label[BTRFS_LABEL_SIZE - 1])
|
|
|
|
- disk_super->label[BTRFS_LABEL_SIZE - 1] = '\0';
|
|
|
|
printk(KERN_INFO "BTRFS: device label %s ", disk_super->label);
|
|
printk(KERN_INFO "BTRFS: device label %s ", disk_super->label);
|
|
} else {
|
|
} else {
|
|
printk(KERN_INFO "BTRFS: device fsid %pU ", disk_super->fsid);
|
|
printk(KERN_INFO "BTRFS: device fsid %pU ", disk_super->fsid);
|
|
@@ -1073,9 +1108,7 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
|
if (!ret && fs_devices_ret)
|
|
if (!ret && fs_devices_ret)
|
|
(*fs_devices_ret)->total_devices = total_devices;
|
|
(*fs_devices_ret)->total_devices = total_devices;
|
|
|
|
|
|
-error_unmap:
|
|
|
|
- kunmap(page);
|
|
|
|
- put_page(page);
|
|
|
|
|
|
+ btrfs_release_disk_super(page);
|
|
|
|
|
|
error_bdev_put:
|
|
error_bdev_put:
|
|
blkdev_put(bdev, flags);
|
|
blkdev_put(bdev, flags);
|
|
@@ -1688,32 +1721,92 @@ out:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-int btrfs_rm_device(struct btrfs_root *root, char *device_path)
|
|
|
|
|
|
+/*
|
|
|
|
+ * Verify that @num_devices satisfies the RAID profile constraints in the whole
|
|
|
|
+ * filesystem. It's up to the caller to adjust that number regarding eg. device
|
|
|
|
+ * replace.
|
|
|
|
+ */
|
|
|
|
+static int btrfs_check_raid_min_devices(struct btrfs_fs_info *fs_info,
|
|
|
|
+ u64 num_devices)
|
|
|
|
+{
|
|
|
|
+ u64 all_avail;
|
|
|
|
+ unsigned seq;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ seq = read_seqbegin(&fs_info->profiles_lock);
|
|
|
|
+
|
|
|
|
+ all_avail = fs_info->avail_data_alloc_bits |
|
|
|
|
+ fs_info->avail_system_alloc_bits |
|
|
|
|
+ fs_info->avail_metadata_alloc_bits;
|
|
|
|
+ } while (read_seqretry(&fs_info->profiles_lock, seq));
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) {
|
|
|
|
+ if (!(all_avail & btrfs_raid_group[i]))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (num_devices < btrfs_raid_array[i].devs_min) {
|
|
|
|
+ int ret = btrfs_raid_mindev_error[i];
|
|
|
|
+
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+struct btrfs_device *btrfs_find_next_active_device(struct btrfs_fs_devices *fs_devs,
|
|
|
|
+ struct btrfs_device *device)
|
|
{
|
|
{
|
|
- struct btrfs_device *device;
|
|
|
|
struct btrfs_device *next_device;
|
|
struct btrfs_device *next_device;
|
|
- struct block_device *bdev;
|
|
|
|
- struct buffer_head *bh = NULL;
|
|
|
|
- struct btrfs_super_block *disk_super;
|
|
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(next_device, &fs_devs->devices, dev_list) {
|
|
|
|
+ if (next_device != device &&
|
|
|
|
+ !next_device->missing && next_device->bdev)
|
|
|
|
+ return next_device;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Helper function to check if the given device is part of s_bdev / latest_bdev
|
|
|
|
+ * and replace it with the provided or the next active device, in the context
|
|
|
|
+ * where this function called, there should be always be another device (or
|
|
|
|
+ * this_dev) which is active.
|
|
|
|
+ */
|
|
|
|
+void btrfs_assign_next_active_device(struct btrfs_fs_info *fs_info,
|
|
|
|
+ struct btrfs_device *device, struct btrfs_device *this_dev)
|
|
|
|
+{
|
|
|
|
+ struct btrfs_device *next_device;
|
|
|
|
+
|
|
|
|
+ if (this_dev)
|
|
|
|
+ next_device = this_dev;
|
|
|
|
+ else
|
|
|
|
+ next_device = btrfs_find_next_active_device(fs_info->fs_devices,
|
|
|
|
+ device);
|
|
|
|
+ ASSERT(next_device);
|
|
|
|
+
|
|
|
|
+ if (fs_info->sb->s_bdev &&
|
|
|
|
+ (fs_info->sb->s_bdev == device->bdev))
|
|
|
|
+ fs_info->sb->s_bdev = next_device->bdev;
|
|
|
|
+
|
|
|
|
+ if (fs_info->fs_devices->latest_bdev == device->bdev)
|
|
|
|
+ fs_info->fs_devices->latest_bdev = next_device->bdev;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid)
|
|
|
|
+{
|
|
|
|
+ struct btrfs_device *device;
|
|
struct btrfs_fs_devices *cur_devices;
|
|
struct btrfs_fs_devices *cur_devices;
|
|
- u64 all_avail;
|
|
|
|
- u64 devid;
|
|
|
|
u64 num_devices;
|
|
u64 num_devices;
|
|
- u8 *dev_uuid;
|
|
|
|
- unsigned seq;
|
|
|
|
int ret = 0;
|
|
int ret = 0;
|
|
bool clear_super = false;
|
|
bool clear_super = false;
|
|
|
|
+ char *dev_name = NULL;
|
|
|
|
|
|
mutex_lock(&uuid_mutex);
|
|
mutex_lock(&uuid_mutex);
|
|
|
|
|
|
- do {
|
|
|
|
- seq = read_seqbegin(&root->fs_info->profiles_lock);
|
|
|
|
-
|
|
|
|
- all_avail = root->fs_info->avail_data_alloc_bits |
|
|
|
|
- root->fs_info->avail_system_alloc_bits |
|
|
|
|
- root->fs_info->avail_metadata_alloc_bits;
|
|
|
|
- } while (read_seqretry(&root->fs_info->profiles_lock, seq));
|
|
|
|
-
|
|
|
|
num_devices = root->fs_info->fs_devices->num_devices;
|
|
num_devices = root->fs_info->fs_devices->num_devices;
|
|
btrfs_dev_replace_lock(&root->fs_info->dev_replace, 0);
|
|
btrfs_dev_replace_lock(&root->fs_info->dev_replace, 0);
|
|
if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) {
|
|
if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) {
|
|
@@ -1722,78 +1815,23 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
|
|
}
|
|
}
|
|
btrfs_dev_replace_unlock(&root->fs_info->dev_replace, 0);
|
|
btrfs_dev_replace_unlock(&root->fs_info->dev_replace, 0);
|
|
|
|
|
|
- if ((all_avail & BTRFS_BLOCK_GROUP_RAID10) && num_devices <= 4) {
|
|
|
|
- ret = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((all_avail & BTRFS_BLOCK_GROUP_RAID1) && num_devices <= 2) {
|
|
|
|
- ret = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET;
|
|
|
|
|
|
+ ret = btrfs_check_raid_min_devices(root->fs_info, num_devices - 1);
|
|
|
|
+ if (ret)
|
|
goto out;
|
|
goto out;
|
|
- }
|
|
|
|
|
|
|
|
- if ((all_avail & BTRFS_BLOCK_GROUP_RAID5) &&
|
|
|
|
- root->fs_info->fs_devices->rw_devices <= 2) {
|
|
|
|
- ret = BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
- if ((all_avail & BTRFS_BLOCK_GROUP_RAID6) &&
|
|
|
|
- root->fs_info->fs_devices->rw_devices <= 3) {
|
|
|
|
- ret = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET;
|
|
|
|
|
|
+ ret = btrfs_find_device_by_devspec(root, devid, device_path,
|
|
|
|
+ &device);
|
|
|
|
+ if (ret)
|
|
goto out;
|
|
goto out;
|
|
- }
|
|
|
|
-
|
|
|
|
- if (strcmp(device_path, "missing") == 0) {
|
|
|
|
- struct list_head *devices;
|
|
|
|
- struct btrfs_device *tmp;
|
|
|
|
-
|
|
|
|
- device = NULL;
|
|
|
|
- devices = &root->fs_info->fs_devices->devices;
|
|
|
|
- /*
|
|
|
|
- * It is safe to read the devices since the volume_mutex
|
|
|
|
- * is held.
|
|
|
|
- */
|
|
|
|
- list_for_each_entry(tmp, devices, dev_list) {
|
|
|
|
- if (tmp->in_fs_metadata &&
|
|
|
|
- !tmp->is_tgtdev_for_dev_replace &&
|
|
|
|
- !tmp->bdev) {
|
|
|
|
- device = tmp;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- bdev = NULL;
|
|
|
|
- bh = NULL;
|
|
|
|
- disk_super = NULL;
|
|
|
|
- if (!device) {
|
|
|
|
- ret = BTRFS_ERROR_DEV_MISSING_NOT_FOUND;
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- ret = btrfs_get_bdev_and_sb(device_path,
|
|
|
|
- FMODE_WRITE | FMODE_EXCL,
|
|
|
|
- root->fs_info->bdev_holder, 0,
|
|
|
|
- &bdev, &bh);
|
|
|
|
- if (ret)
|
|
|
|
- goto out;
|
|
|
|
- disk_super = (struct btrfs_super_block *)bh->b_data;
|
|
|
|
- devid = btrfs_stack_device_id(&disk_super->dev_item);
|
|
|
|
- dev_uuid = disk_super->dev_item.uuid;
|
|
|
|
- device = btrfs_find_device(root->fs_info, devid, dev_uuid,
|
|
|
|
- disk_super->fsid);
|
|
|
|
- if (!device) {
|
|
|
|
- ret = -ENOENT;
|
|
|
|
- goto error_brelse;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
|
|
if (device->is_tgtdev_for_dev_replace) {
|
|
if (device->is_tgtdev_for_dev_replace) {
|
|
ret = BTRFS_ERROR_DEV_TGT_REPLACE;
|
|
ret = BTRFS_ERROR_DEV_TGT_REPLACE;
|
|
- goto error_brelse;
|
|
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) {
|
|
if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) {
|
|
ret = BTRFS_ERROR_DEV_ONLY_WRITABLE;
|
|
ret = BTRFS_ERROR_DEV_ONLY_WRITABLE;
|
|
- goto error_brelse;
|
|
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
if (device->writeable) {
|
|
if (device->writeable) {
|
|
@@ -1801,6 +1839,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
|
|
list_del_init(&device->dev_alloc_list);
|
|
list_del_init(&device->dev_alloc_list);
|
|
device->fs_devices->rw_devices--;
|
|
device->fs_devices->rw_devices--;
|
|
unlock_chunks(root);
|
|
unlock_chunks(root);
|
|
|
|
+ dev_name = kstrdup(device->name->str, GFP_KERNEL);
|
|
|
|
+ if (!dev_name) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto error_undo;
|
|
|
|
+ }
|
|
clear_super = true;
|
|
clear_super = true;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1842,12 +1885,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
|
|
if (device->missing)
|
|
if (device->missing)
|
|
device->fs_devices->missing_devices--;
|
|
device->fs_devices->missing_devices--;
|
|
|
|
|
|
- next_device = list_entry(root->fs_info->fs_devices->devices.next,
|
|
|
|
- struct btrfs_device, dev_list);
|
|
|
|
- if (device->bdev == root->fs_info->sb->s_bdev)
|
|
|
|
- root->fs_info->sb->s_bdev = next_device->bdev;
|
|
|
|
- if (device->bdev == root->fs_info->fs_devices->latest_bdev)
|
|
|
|
- root->fs_info->fs_devices->latest_bdev = next_device->bdev;
|
|
|
|
|
|
+ btrfs_assign_next_active_device(root->fs_info, device, NULL);
|
|
|
|
|
|
if (device->bdev) {
|
|
if (device->bdev) {
|
|
device->fs_devices->open_devices--;
|
|
device->fs_devices->open_devices--;
|
|
@@ -1883,63 +1921,23 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path)
|
|
* at this point, the device is zero sized. We want to
|
|
* at this point, the device is zero sized. We want to
|
|
* remove it from the devices list and zero out the old super
|
|
* remove it from the devices list and zero out the old super
|
|
*/
|
|
*/
|
|
- if (clear_super && disk_super) {
|
|
|
|
- u64 bytenr;
|
|
|
|
- int i;
|
|
|
|
-
|
|
|
|
- /* make sure this device isn't detected as part of
|
|
|
|
- * the FS anymore
|
|
|
|
- */
|
|
|
|
- memset(&disk_super->magic, 0, sizeof(disk_super->magic));
|
|
|
|
- set_buffer_dirty(bh);
|
|
|
|
- sync_dirty_buffer(bh);
|
|
|
|
-
|
|
|
|
- /* clear the mirror copies of super block on the disk
|
|
|
|
- * being removed, 0th copy is been taken care above and
|
|
|
|
- * the below would take of the rest
|
|
|
|
- */
|
|
|
|
- for (i = 1; i < BTRFS_SUPER_MIRROR_MAX; i++) {
|
|
|
|
- bytenr = btrfs_sb_offset(i);
|
|
|
|
- if (bytenr + BTRFS_SUPER_INFO_SIZE >=
|
|
|
|
- i_size_read(bdev->bd_inode))
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- brelse(bh);
|
|
|
|
- bh = __bread(bdev, bytenr / 4096,
|
|
|
|
- BTRFS_SUPER_INFO_SIZE);
|
|
|
|
- if (!bh)
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- disk_super = (struct btrfs_super_block *)bh->b_data;
|
|
|
|
-
|
|
|
|
- if (btrfs_super_bytenr(disk_super) != bytenr ||
|
|
|
|
- btrfs_super_magic(disk_super) != BTRFS_MAGIC) {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- memset(&disk_super->magic, 0,
|
|
|
|
- sizeof(disk_super->magic));
|
|
|
|
- set_buffer_dirty(bh);
|
|
|
|
- sync_dirty_buffer(bh);
|
|
|
|
|
|
+ if (clear_super) {
|
|
|
|
+ struct block_device *bdev;
|
|
|
|
+
|
|
|
|
+ bdev = blkdev_get_by_path(dev_name, FMODE_READ | FMODE_EXCL,
|
|
|
|
+ root->fs_info->bdev_holder);
|
|
|
|
+ if (!IS_ERR(bdev)) {
|
|
|
|
+ btrfs_scratch_superblocks(bdev, dev_name);
|
|
|
|
+ blkdev_put(bdev, FMODE_READ | FMODE_EXCL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- ret = 0;
|
|
|
|
-
|
|
|
|
- if (bdev) {
|
|
|
|
- /* Notify udev that device has changed */
|
|
|
|
- btrfs_kobject_uevent(bdev, KOBJ_CHANGE);
|
|
|
|
-
|
|
|
|
- /* Update ctime/mtime for device path for libblkid */
|
|
|
|
- update_dev_time(device_path);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-error_brelse:
|
|
|
|
- brelse(bh);
|
|
|
|
- if (bdev)
|
|
|
|
- blkdev_put(bdev, FMODE_READ | FMODE_EXCL);
|
|
|
|
out:
|
|
out:
|
|
|
|
+ kfree(dev_name);
|
|
|
|
+
|
|
mutex_unlock(&uuid_mutex);
|
|
mutex_unlock(&uuid_mutex);
|
|
return ret;
|
|
return ret;
|
|
|
|
+
|
|
error_undo:
|
|
error_undo:
|
|
if (device->writeable) {
|
|
if (device->writeable) {
|
|
lock_chunks(root);
|
|
lock_chunks(root);
|
|
@@ -1948,7 +1946,7 @@ error_undo:
|
|
device->fs_devices->rw_devices++;
|
|
device->fs_devices->rw_devices++;
|
|
unlock_chunks(root);
|
|
unlock_chunks(root);
|
|
}
|
|
}
|
|
- goto error_brelse;
|
|
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
|
|
void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info,
|
|
void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info,
|
|
@@ -2017,8 +2015,6 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_fs_info *fs_info,
|
|
void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
|
|
void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
|
|
struct btrfs_device *tgtdev)
|
|
struct btrfs_device *tgtdev)
|
|
{
|
|
{
|
|
- struct btrfs_device *next_device;
|
|
|
|
-
|
|
|
|
mutex_lock(&uuid_mutex);
|
|
mutex_lock(&uuid_mutex);
|
|
WARN_ON(!tgtdev);
|
|
WARN_ON(!tgtdev);
|
|
mutex_lock(&fs_info->fs_devices->device_list_mutex);
|
|
mutex_lock(&fs_info->fs_devices->device_list_mutex);
|
|
@@ -2030,12 +2026,8 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
|
|
|
|
|
|
fs_info->fs_devices->num_devices--;
|
|
fs_info->fs_devices->num_devices--;
|
|
|
|
|
|
- next_device = list_entry(fs_info->fs_devices->devices.next,
|
|
|
|
- struct btrfs_device, dev_list);
|
|
|
|
- if (tgtdev->bdev == fs_info->sb->s_bdev)
|
|
|
|
- fs_info->sb->s_bdev = next_device->bdev;
|
|
|
|
- if (tgtdev->bdev == fs_info->fs_devices->latest_bdev)
|
|
|
|
- fs_info->fs_devices->latest_bdev = next_device->bdev;
|
|
|
|
|
|
+ btrfs_assign_next_active_device(fs_info, tgtdev, NULL);
|
|
|
|
+
|
|
list_del_rcu(&tgtdev->dev_list);
|
|
list_del_rcu(&tgtdev->dev_list);
|
|
|
|
|
|
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
|
|
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
|
|
@@ -2109,6 +2101,31 @@ int btrfs_find_device_missing_or_by_path(struct btrfs_root *root,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Lookup a device given by device id, or the path if the id is 0.
|
|
|
|
+ */
|
|
|
|
+int btrfs_find_device_by_devspec(struct btrfs_root *root, u64 devid,
|
|
|
|
+ char *devpath,
|
|
|
|
+ struct btrfs_device **device)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (devid) {
|
|
|
|
+ ret = 0;
|
|
|
|
+ *device = btrfs_find_device(root->fs_info, devid, NULL,
|
|
|
|
+ NULL);
|
|
|
|
+ if (!*device)
|
|
|
|
+ ret = -ENOENT;
|
|
|
|
+ } else {
|
|
|
|
+ if (!devpath || !devpath[0])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ ret = btrfs_find_device_missing_or_by_path(root, devpath,
|
|
|
|
+ device);
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* does all the dirty work required for changing file system's UUID.
|
|
* does all the dirty work required for changing file system's UUID.
|
|
*/
|
|
*/
|