From 96f3136e51580ed68a2952341c8b9e2d7f853472 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:46 +0800 Subject: [PATCH 01/30] Btrfs: rename btrfs_sysfs_add_one to btrfs_sysfs_add_mounted Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 +- fs/btrfs/disk-io.c | 2 +- fs/btrfs/sysfs.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 938efe33be8092..afce3069697a8f 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -4004,7 +4004,7 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, /* sysfs.c */ int btrfs_init_sysfs(void); void btrfs_exit_sysfs(void); -int btrfs_sysfs_add_one(struct btrfs_fs_info *fs_info); +int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info); void btrfs_sysfs_remove_one(struct btrfs_fs_info *fs_info); /* xattr.c */ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 295795aebe0b42..4622d38fa56e7d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2933,7 +2933,7 @@ int open_ctree(struct super_block *sb, goto fail_fsdev_sysfs; } - ret = btrfs_sysfs_add_one(fs_info); + ret = btrfs_sysfs_add_mounted(fs_info); if (ret) { pr_err("BTRFS: failed to init sysfs interface: %d\n", ret); goto fail_fsdev_sysfs; diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 603b0cc2b9bbf6..cabf840f6b37e1 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -736,7 +736,7 @@ int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, return error; } -int btrfs_sysfs_add_one(struct btrfs_fs_info *fs_info) +int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) { int error; struct btrfs_fs_devices *fs_devs = fs_info->fs_devices; From 6618a59bfc0a0490f2a51df37c67878e23285670 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:47 +0800 Subject: [PATCH 02/30] Btrfs: rename btrfs_sysfs_remove_one to btrfs_sysfs_remove_mounted Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/ctree.h | 2 +- fs/btrfs/disk-io.c | 4 ++-- fs/btrfs/sysfs.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index afce3069697a8f..44840630727248 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -4005,7 +4005,7 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, int btrfs_init_sysfs(void); void btrfs_exit_sysfs(void); int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info); -void btrfs_sysfs_remove_one(struct btrfs_fs_info *fs_info); +void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info); /* xattr.c */ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 4622d38fa56e7d..76734732eca670 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3113,7 +3113,7 @@ int open_ctree(struct super_block *sb, filemap_write_and_wait(fs_info->btree_inode->i_mapping); fail_sysfs: - btrfs_sysfs_remove_one(fs_info); + btrfs_sysfs_remove_mounted(fs_info); fail_fsdev_sysfs: btrfs_sysfs_remove_fsid(fs_info->fs_devices); @@ -3788,7 +3788,7 @@ void close_ctree(struct btrfs_root *root) percpu_counter_sum(&fs_info->delalloc_bytes)); } - btrfs_sysfs_remove_one(fs_info); + btrfs_sysfs_remove_mounted(fs_info); btrfs_sysfs_remove_fsid(fs_info->fs_devices); btrfs_free_fs_roots(fs_info); diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index cabf840f6b37e1..095a302417ef55 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -545,7 +545,7 @@ void btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs) } } -void btrfs_sysfs_remove_one(struct btrfs_fs_info *fs_info) +void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info) { btrfs_reset_fs_info_ptr(fs_info); @@ -776,7 +776,7 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) return 0; failure: - btrfs_sysfs_remove_one(fs_info); + btrfs_sysfs_remove_mounted(fs_info); return error; } From e3bd6973bcf134a56786a8bd248d1740249352ec Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:48 +0800 Subject: [PATCH 03/30] Btrfs: rename btrfs_kobj_add_device to btrfs_sysfs_add_device_link Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 2 +- fs/btrfs/sysfs.c | 4 ++-- fs/btrfs/sysfs.h | 2 +- fs/btrfs/volumes.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index e54dd5905cee17..200c972c45d447 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -375,7 +375,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root, WARN_ON(!tgt_device); dev_replace->tgtdev = tgt_device; - ret = btrfs_kobj_add_device(tgt_device->fs_devices, tgt_device); + ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); if (ret) btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 095a302417ef55..df67f6bee2a95c 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -683,7 +683,7 @@ int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs) return 0; } -int btrfs_kobj_add_device(struct btrfs_fs_devices *fs_devices, +int btrfs_sysfs_add_device_link(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device) { int error = 0; @@ -744,7 +744,7 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) btrfs_set_fs_info_ptr(fs_info); - error = btrfs_kobj_add_device(fs_devs, NULL); + error = btrfs_sysfs_add_device_link(fs_devs, NULL); if (error) return error; diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index 6392527bcc15d5..6529680f0b4225 100644 --- a/fs/btrfs/sysfs.h +++ b/fs/btrfs/sysfs.h @@ -82,7 +82,7 @@ char *btrfs_printable_features(enum btrfs_feature_set set, u64 flags); extern const char * const btrfs_feature_set_names[3]; extern struct kobj_type space_info_ktype; extern struct kobj_type btrfs_raid_ktype; -int btrfs_kobj_add_device(struct btrfs_fs_devices *fs_devices, +int btrfs_sysfs_add_device_link(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device); int btrfs_kobj_rm_device(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 6fc735869c186c..d22029ad9b1e77 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2309,7 +2309,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) tmp + 1); /* add sysfs device entry */ - btrfs_kobj_add_device(root->fs_info->fs_devices, device); + btrfs_sysfs_add_device_link(root->fs_info->fs_devices, device); /* * we've got more storage, clear any full flags on the space From 325760404820e070bb20be0ce57e8d684d69a2ac Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:49 +0800 Subject: [PATCH 04/30] Btrfs: rename btrfs_kobj_rm_device to btrfs_sysfs_rm_device_link Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 2 +- fs/btrfs/sysfs.c | 6 +++--- fs/btrfs/sysfs.h | 2 +- fs/btrfs/volumes.c | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 200c972c45d447..7aed8a8ce69476 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -586,7 +586,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, mutex_unlock(&uuid_mutex); /* replace the sysfs entry */ - btrfs_kobj_rm_device(fs_info->fs_devices, src_device); + btrfs_sysfs_rm_device_link(fs_info->fs_devices, src_device); btrfs_rm_dev_replace_free_srcdev(fs_info, src_device); /* write back the superblocks */ diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index df67f6bee2a95c..52319d17e68599 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -557,7 +557,7 @@ void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info) addrm_unknown_feature_attrs(fs_info, false); sysfs_remove_group(&fs_info->fs_devices->super_kobj, &btrfs_feature_attr_group); sysfs_remove_files(&fs_info->fs_devices->super_kobj, btrfs_attrs); - btrfs_kobj_rm_device(fs_info->fs_devices, NULL); + btrfs_sysfs_rm_device_link(fs_info->fs_devices, NULL); } const char * const btrfs_feature_set_names[3] = { @@ -637,7 +637,7 @@ static void init_feature_attrs(void) /* when one_device is NULL, it removes all device links */ -int btrfs_kobj_rm_device(struct btrfs_fs_devices *fs_devices, +int btrfs_sysfs_rm_device_link(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device) { struct hd_struct *disk; @@ -750,7 +750,7 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) error = sysfs_create_files(super_kobj, btrfs_attrs); if (error) { - btrfs_kobj_rm_device(fs_devs, NULL); + btrfs_sysfs_rm_device_link(fs_devs, NULL); return error; } diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index 6529680f0b4225..9c09522125a6b2 100644 --- a/fs/btrfs/sysfs.h +++ b/fs/btrfs/sysfs.h @@ -84,7 +84,7 @@ extern struct kobj_type space_info_ktype; extern struct kobj_type btrfs_raid_ktype; int btrfs_sysfs_add_device_link(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device); -int btrfs_kobj_rm_device(struct btrfs_fs_devices *fs_devices, +int btrfs_sysfs_rm_device_link(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device); int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, struct kobject *parent); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d22029ad9b1e77..003d3ddeb78b26 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1801,7 +1801,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) if (device->bdev) { device->fs_devices->open_devices--; /* remove sysfs entry */ - btrfs_kobj_rm_device(root->fs_info->fs_devices, device); + btrfs_sysfs_rm_device_link(root->fs_info->fs_devices, device); } call_rcu(&device->rcu, free_device); @@ -1971,7 +1971,7 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, WARN_ON(!tgtdev); mutex_lock(&fs_info->fs_devices->device_list_mutex); - btrfs_kobj_rm_device(fs_info->fs_devices, tgtdev); + btrfs_sysfs_rm_device_link(fs_info->fs_devices, tgtdev); if (tgtdev->bdev) { btrfs_scratch_superblock(tgtdev); @@ -2388,7 +2388,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) error_trans: btrfs_end_transaction(trans, root); rcu_string_free(device->name); - btrfs_kobj_rm_device(root->fs_info->fs_devices, device); + btrfs_sysfs_rm_device_link(root->fs_info->fs_devices, device); kfree(device); error: blkdev_put(bdev, FMODE_EXCL); From c1b7e474595b1b3a4463450cc22aa18b926093a3 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:50 +0800 Subject: [PATCH 05/30] Btrfs: rename super_kobj to fsid_kobj Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/sysfs.c | 36 ++++++++++++++++++------------------ fs/btrfs/volumes.c | 2 +- fs/btrfs/volumes.h | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 52319d17e68599..e0ac85949067c3 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -437,24 +437,24 @@ static const struct attribute *btrfs_attrs[] = { NULL, }; -static void btrfs_release_super_kobj(struct kobject *kobj) +static void btrfs_release_fsid_kobj(struct kobject *kobj) { struct btrfs_fs_devices *fs_devs = to_fs_devs(kobj); - memset(&fs_devs->super_kobj, 0, sizeof(struct kobject)); + memset(&fs_devs->fsid_kobj, 0, sizeof(struct kobject)); complete(&fs_devs->kobj_unregister); } static struct kobj_type btrfs_ktype = { .sysfs_ops = &kobj_sysfs_ops, - .release = btrfs_release_super_kobj, + .release = btrfs_release_fsid_kobj, }; static inline struct btrfs_fs_devices *to_fs_devs(struct kobject *kobj) { if (kobj->ktype != &btrfs_ktype) return NULL; - return container_of(kobj, struct btrfs_fs_devices, super_kobj); + return container_of(kobj, struct btrfs_fs_devices, fsid_kobj); } static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj) @@ -502,12 +502,12 @@ static int addrm_unknown_feature_attrs(struct btrfs_fs_info *fs_info, bool add) attrs[0] = &fa->kobj_attr.attr; if (add) { int ret; - ret = sysfs_merge_group(&fs_info->fs_devices->super_kobj, + ret = sysfs_merge_group(&fs_info->fs_devices->fsid_kobj, &agroup); if (ret) return ret; } else - sysfs_unmerge_group(&fs_info->fs_devices->super_kobj, + sysfs_unmerge_group(&fs_info->fs_devices->fsid_kobj, &agroup); } @@ -523,9 +523,9 @@ static void __btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs) fs_devs->device_dir_kobj = NULL; } - if (fs_devs->super_kobj.state_initialized) { - kobject_del(&fs_devs->super_kobj); - kobject_put(&fs_devs->super_kobj); + if (fs_devs->fsid_kobj.state_initialized) { + kobject_del(&fs_devs->fsid_kobj); + kobject_put(&fs_devs->fsid_kobj); wait_for_completion(&fs_devs->kobj_unregister); } } @@ -555,8 +555,8 @@ void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info) kobject_put(fs_info->space_info_kobj); } addrm_unknown_feature_attrs(fs_info, false); - sysfs_remove_group(&fs_info->fs_devices->super_kobj, &btrfs_feature_attr_group); - sysfs_remove_files(&fs_info->fs_devices->super_kobj, btrfs_attrs); + sysfs_remove_group(&fs_info->fs_devices->fsid_kobj, &btrfs_feature_attr_group); + sysfs_remove_files(&fs_info->fs_devices->fsid_kobj, btrfs_attrs); btrfs_sysfs_rm_device_link(fs_info->fs_devices, NULL); } @@ -675,7 +675,7 @@ int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs) { if (!fs_devs->device_dir_kobj) fs_devs->device_dir_kobj = kobject_create_and_add("devices", - &fs_devs->super_kobj); + &fs_devs->fsid_kobj); if (!fs_devs->device_dir_kobj) return -ENOMEM; @@ -730,8 +730,8 @@ int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, int error; init_completion(&fs_devs->kobj_unregister); - fs_devs->super_kobj.kset = btrfs_kset; - error = kobject_init_and_add(&fs_devs->super_kobj, + fs_devs->fsid_kobj.kset = btrfs_kset; + error = kobject_init_and_add(&fs_devs->fsid_kobj, &btrfs_ktype, parent, "%pU", fs_devs->fsid); return error; } @@ -740,7 +740,7 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) { int error; struct btrfs_fs_devices *fs_devs = fs_info->fs_devices; - struct kobject *super_kobj = &fs_devs->super_kobj; + struct kobject *fsid_kobj = &fs_devs->fsid_kobj; btrfs_set_fs_info_ptr(fs_info); @@ -748,13 +748,13 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) if (error) return error; - error = sysfs_create_files(super_kobj, btrfs_attrs); + error = sysfs_create_files(fsid_kobj, btrfs_attrs); if (error) { btrfs_sysfs_rm_device_link(fs_devs, NULL); return error; } - error = sysfs_create_group(super_kobj, + error = sysfs_create_group(fsid_kobj, &btrfs_feature_attr_group); if (error) goto failure; @@ -764,7 +764,7 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) goto failure; fs_info->space_info_kobj = kobject_create_and_add("allocation", - super_kobj); + fsid_kobj); if (!fs_info->space_info_kobj) { error = -ENOMEM; goto failure; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 003d3ddeb78b26..643879208f05c3 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2350,7 +2350,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) */ snprintf(fsid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU", root->fs_info->fsid); - if (kobject_rename(&root->fs_info->fs_devices->super_kobj, + if (kobject_rename(&root->fs_info->fs_devices->fsid_kobj, fsid_buf)) pr_warn("BTRFS: sysfs: failed to create fsid for sprout\n"); } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 2ca784a14e84bc..413a70c66f4cff 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -256,7 +256,7 @@ struct btrfs_fs_devices { struct btrfs_fs_info *fs_info; /* sysfs kobjects */ - struct kobject super_kobj; + struct kobject fsid_kobj; struct kobject *device_dir_kobj; struct completion kobj_unregister; }; From 92fc03fbdcbe2523be3f7e6b8e95fee9563a10d2 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:51 +0800 Subject: [PATCH 06/30] Btrfs: SB read failure should return EIO for __bread failure This will return EIO when __bread() fails to read SB, instead of EINVAL. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 18 +++++++++++++++--- fs/btrfs/volumes.c | 8 ++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 76734732eca670..357d9f9c99688f 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2653,8 +2653,8 @@ int open_ctree(struct super_block *sb, * Read super block and check the signature bytes only */ bh = btrfs_read_dev_super(fs_devices->latest_bdev); - if (!bh) { - err = -EINVAL; + if (IS_ERR(bh)) { + err = PTR_ERR(bh); goto fail_alloc; } @@ -3196,6 +3196,7 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev) int i; u64 transid = 0; u64 bytenr; + int ret = -EINVAL; /* we would like to check all the supers, but that would make * a btrfs mount succeed after a mkfs from a different FS. @@ -3209,13 +3210,20 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev) break; bh = __bread(bdev, bytenr / 4096, BTRFS_SUPER_INFO_SIZE); - if (!bh) + /* + * If we fail to read from the underlying devices, as of now + * the best option we have is to mark it EIO. + */ + if (!bh) { + ret = -EIO; continue; + } super = (struct btrfs_super_block *)bh->b_data; if (btrfs_super_bytenr(super) != bytenr || btrfs_super_magic(super) != BTRFS_MAGIC) { brelse(bh); + ret = -EINVAL; continue; } @@ -3227,6 +3235,10 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev) brelse(bh); } } + + if (!latest) + return ERR_PTR(ret); + return latest; } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 643879208f05c3..e8a618261218f5 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -211,8 +211,8 @@ btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags, void *holder, } invalidate_bdev(*bdev); *bh = btrfs_read_dev_super(*bdev); - if (!*bh) { - ret = -EINVAL; + if (IS_ERR(*bh)) { + ret = PTR_ERR(*bh); blkdev_put(*bdev, flags); goto error; } @@ -6746,8 +6746,8 @@ int btrfs_scratch_superblock(struct btrfs_device *device) struct btrfs_super_block *disk_super; bh = btrfs_read_dev_super(device->bdev); - if (!bh) - return -EINVAL; + if (IS_ERR(bh)) + return PTR_ERR(bh); disk_super = (struct btrfs_super_block *)bh->b_data; memset(&disk_super->magic, 0, sizeof(disk_super->magic)); From 57d816a15ba2c2690c57635134bc01cf4da4623c Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:52 +0800 Subject: [PATCH 07/30] Btrfs: __btrfs_std_error() logic should be consistent w/out CONFIG_PRINTK defined error handling logic behaves differently with or without CONFIG_PRINTK defined, since there are two copies of the same function which a bit of different logic One, when CONFIG_PRINTK is defined, code is __btrfs_std_error(..) { :: save_error_info(fs_info); if (sb->s_flags & MS_BORN) btrfs_handle_error(fs_info); } and two when CONFIG_PRINTK is not defined, the code is __btrfs_std_error(..) { :: if (sb->s_flags & MS_BORN) { save_error_info(fs_info); btrfs_handle_error(fs_info); } } I doubt if this was intentional ? and appear to have caused since we maintain two copies of the same function and they got diverged with commits. Now to decide which logic is correct reviewed changes as below, 533574c6bc30cf526cc1c41bde050c854a945efb Commit added two copies of this function cf79ffb5b79e8a2b587fbf218809e691bb396c98 Commit made change to only one copy of the function and to the copy when CONFIG_PRINTK is defined. To fix this, instead of maintaining two copies of same function approach, maintain single function, and just put the extra portion of the code under CONFIG_PRINTK define. This patch just does that. And keeps code of with CONFIG_PRINTK defined. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/super.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 11d1eab9234dc8..b23d49daa1a215 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -130,7 +130,6 @@ static void btrfs_handle_error(struct btrfs_fs_info *fs_info) } } -#ifdef CONFIG_PRINTK /* * __btrfs_std_error decodes expected errors from the caller and * invokes the approciate error response. @@ -140,7 +139,9 @@ void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, unsigned int line, int errno, const char *fmt, ...) { struct super_block *sb = fs_info->sb; +#ifdef CONFIG_PRINTK const char *errstr; +#endif /* * Special case: if the error is EROFS, and we're already @@ -149,6 +150,7 @@ void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, if (errno == -EROFS && (sb->s_flags & MS_RDONLY)) return; +#ifdef CONFIG_PRINTK errstr = btrfs_decode_error(errno); if (fmt) { struct va_format vaf; @@ -166,6 +168,7 @@ void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, printk(KERN_CRIT "BTRFS: error (device %s) in %s:%d: errno=%d %s\n", sb->s_id, function, line, errno, errstr); } +#endif /* Don't go through full error handling during mount */ save_error_info(fs_info); @@ -173,6 +176,7 @@ void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, btrfs_handle_error(fs_info); } +#ifdef CONFIG_PRINTK static const char * const logtypes[] = { "emergency", "alert", @@ -212,27 +216,6 @@ void btrfs_printk(const struct btrfs_fs_info *fs_info, const char *fmt, ...) va_end(args); } - -#else - -void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function, - unsigned int line, int errno, const char *fmt, ...) -{ - struct super_block *sb = fs_info->sb; - - /* - * Special case: if the error is EROFS, and we're already - * under MS_RDONLY, then it is safe here. - */ - if (errno == -EROFS && (sb->s_flags & MS_RDONLY)) - return; - - /* Don't go through full error handling during mount */ - if (sb->s_flags & MS_BORN) { - save_error_info(fs_info); - btrfs_handle_error(fs_info); - } -} #endif /* From a4553fefb59cb0336f543fa567170b47e90142a9 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 25 Sep 2015 14:43:01 +0800 Subject: [PATCH 08/30] Btrfs: consolidate btrfs_error() to btrfs_std_error() btrfs_error() and btrfs_std_error() does the same thing and calls _btrfs_std_error(), so consolidate them together. And the main motivation is that btrfs_error() is closely named with btrfs_err(), one handles error action the other is to log the error, so don't closely name them. Signed-off-by: Anand Jain Suggested-by: David Sterba Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ctree.c | 6 +++--- fs/btrfs/ctree.h | 9 +-------- fs/btrfs/disk-io.c | 8 ++++---- fs/btrfs/extent-tree.c | 2 +- fs/btrfs/inode-item.c | 2 +- fs/btrfs/ioctl.c | 2 +- fs/btrfs/relocation.c | 2 +- fs/btrfs/root-tree.c | 4 ++-- fs/btrfs/transaction.c | 2 +- fs/btrfs/tree-log.c | 8 ++++---- fs/btrfs/volumes.c | 14 +++++++------- 11 files changed, 26 insertions(+), 33 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 5f745eadf77dd0..1063315fb38737 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -1011,7 +1011,7 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, return ret; if (refs == 0) { ret = -EROFS; - btrfs_std_error(root->fs_info, ret); + btrfs_std_error(root->fs_info, ret, NULL); return ret; } } else { @@ -1927,7 +1927,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, child = read_node_slot(root, mid, 0); if (!child) { ret = -EROFS; - btrfs_std_error(root->fs_info, ret); + btrfs_std_error(root->fs_info, ret, NULL); goto enospc; } @@ -2030,7 +2030,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, */ if (!left) { ret = -EROFS; - btrfs_std_error(root->fs_info, ret); + btrfs_std_error(root->fs_info, ret, NULL); goto enospc; } wret = balance_node_right(trans, root, mid, left); diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 44840630727248..a86051e332ffdf 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -4127,14 +4127,7 @@ do { \ __LINE__, (errno)); \ } while (0) -#define btrfs_std_error(fs_info, errno) \ -do { \ - if ((errno)) \ - __btrfs_std_error((fs_info), __func__, \ - __LINE__, (errno), NULL); \ -} while (0) - -#define btrfs_error(fs_info, errno, fmt, args...) \ +#define btrfs_std_error(fs_info, errno, fmt, args...) \ do { \ __btrfs_std_error((fs_info), __func__, __LINE__, \ (errno), fmt, ##args); \ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 357d9f9c99688f..0f8e33f2bceabe 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2377,7 +2377,7 @@ static int btrfs_replay_log(struct btrfs_fs_info *fs_info, /* returns with log_tree_root freed on success */ ret = btrfs_recover_log_trees(log_tree_root); if (ret) { - btrfs_error(tree_root->fs_info, ret, + btrfs_std_error(tree_root->fs_info, ret, "Failed to recover log tree"); free_extent_buffer(log_tree_root->node); kfree(log_tree_root); @@ -3556,7 +3556,7 @@ static int write_all_supers(struct btrfs_root *root, int max_mirrors) if (ret) { mutex_unlock( &root->fs_info->fs_devices->device_list_mutex); - btrfs_error(root->fs_info, ret, + btrfs_std_error(root->fs_info, ret, "errors while submitting device barriers."); return ret; } @@ -3596,7 +3596,7 @@ static int write_all_supers(struct btrfs_root *root, int max_mirrors) mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); /* FUA is masked off if unsupported and can't be the reason */ - btrfs_error(root->fs_info, -EIO, + btrfs_std_error(root->fs_info, -EIO, "%d errors while writing supers", total_errors); return -EIO; } @@ -3614,7 +3614,7 @@ static int write_all_supers(struct btrfs_root *root, int max_mirrors) } mutex_unlock(&root->fs_info->fs_devices->device_list_mutex); if (total_errors > max_errors) { - btrfs_error(root->fs_info, -EIO, + btrfs_std_error(root->fs_info, -EIO, "%d errors while writing supers", total_errors); return -EIO; } diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 9f960420133307..5069f1b7339532 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -8688,7 +8688,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root, if (!for_reloc && root_dropped == false) btrfs_add_dead_root(root); if (err && err != -EAGAIN) - btrfs_std_error(root->fs_info, err); + btrfs_std_error(root->fs_info, err, NULL); return err; } diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c index 265e03c73f4daa..be4d22a5022fa0 100644 --- a/fs/btrfs/inode-item.c +++ b/fs/btrfs/inode-item.c @@ -157,7 +157,7 @@ static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans, */ if (!btrfs_find_name_in_ext_backref(path, ref_objectid, name, name_len, &extref)) { - btrfs_std_error(root->fs_info, -ENOENT); + btrfs_std_error(root->fs_info, -ENOENT, NULL); ret = -EROFS; goto out; } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 0adf5422fce9d4..f704d1c79739aa 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4806,7 +4806,7 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg) /* update qgroup status and info */ err = btrfs_run_qgroups(trans, root->fs_info); if (err < 0) - btrfs_error(root->fs_info, ret, + btrfs_std_error(root->fs_info, ret, "failed to update qgroup status and info\n"); err = btrfs_end_transaction(trans, root); if (err && !ret) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 303babeef50579..58ede0a564569e 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -2418,7 +2418,7 @@ void merge_reloc_roots(struct reloc_control *rc) } out: if (ret) { - btrfs_std_error(root->fs_info, ret); + btrfs_std_error(root->fs_info, ret, NULL); if (!list_empty(&reloc_roots)) free_reloc_roots(&reloc_roots); diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 360a728a639fec..6d90851af4f017 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -283,7 +283,7 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) trans = btrfs_join_transaction(tree_root); if (IS_ERR(trans)) { err = PTR_ERR(trans); - btrfs_error(tree_root->fs_info, err, + btrfs_std_error(tree_root->fs_info, err, "Failed to start trans to delete " "orphan item"); break; @@ -292,7 +292,7 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) root_key.objectid); btrfs_end_transaction(trans, tree_root); if (err) { - btrfs_error(tree_root->fs_info, err, + btrfs_std_error(tree_root->fs_info, err, "Failed to delete root orphan " "item"); break; diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 74bc3338418be3..3482d9eeb62df6 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -2135,7 +2135,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ret = btrfs_write_and_wait_transaction(trans, root); if (ret) { - btrfs_error(root->fs_info, ret, + btrfs_std_error(root->fs_info, ret, "Error while writing out transaction"); mutex_unlock(&root->fs_info->tree_log_mutex); goto scrub_continue; diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 1bbaace733838e..c3f9a9c71f28fa 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -5314,7 +5314,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree) ret = walk_log_tree(trans, log_root_tree, &wc); if (ret) { - btrfs_error(fs_info, ret, "Failed to pin buffers while " + btrfs_std_error(fs_info, ret, "Failed to pin buffers while " "recovering log root tree."); goto error; } @@ -5328,7 +5328,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree) ret = btrfs_search_slot(NULL, log_root_tree, &key, path, 0, 0); if (ret < 0) { - btrfs_error(fs_info, ret, + btrfs_std_error(fs_info, ret, "Couldn't find tree log root."); goto error; } @@ -5346,7 +5346,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree) log = btrfs_read_fs_root(log_root_tree, &found_key); if (IS_ERR(log)) { ret = PTR_ERR(log); - btrfs_error(fs_info, ret, + btrfs_std_error(fs_info, ret, "Couldn't read tree log root."); goto error; } @@ -5361,7 +5361,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree) free_extent_buffer(log->node); free_extent_buffer(log->commit_root); kfree(log); - btrfs_error(fs_info, ret, "Couldn't read target root " + btrfs_std_error(fs_info, ret, "Couldn't read target root " "for tree log recovery."); goto error; } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index e8a618261218f5..99f294cfdcc046 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1402,7 +1402,7 @@ static int btrfs_free_dev_extent(struct btrfs_trans_handle *trans, extent = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_extent); } else { - btrfs_error(root->fs_info, ret, "Slot search failed"); + btrfs_std_error(root->fs_info, ret, "Slot search failed"); goto out; } @@ -1410,7 +1410,7 @@ static int btrfs_free_dev_extent(struct btrfs_trans_handle *trans, ret = btrfs_del_item(trans, root, path); if (ret) { - btrfs_error(root->fs_info, ret, + btrfs_std_error(root->fs_info, ret, "Failed to remove dev extent item"); } else { trans->transaction->have_free_bgs = 1; @@ -2368,7 +2368,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) ret = btrfs_relocate_sys_chunks(root); if (ret < 0) - btrfs_error(root->fs_info, ret, + btrfs_std_error(root->fs_info, ret, "Failed to relocate sys chunks after " "device initialization. This can be fixed " "using the \"btrfs balance\" command."); @@ -2613,7 +2613,7 @@ static int btrfs_free_chunk(struct btrfs_trans_handle *trans, if (ret < 0) goto out; else if (ret > 0) { /* Logic error or corruption */ - btrfs_error(root->fs_info, -ENOENT, + btrfs_std_error(root->fs_info, -ENOENT, "Failed lookup while freeing chunk."); ret = -ENOENT; goto out; @@ -2621,7 +2621,7 @@ static int btrfs_free_chunk(struct btrfs_trans_handle *trans, ret = btrfs_del_item(trans, root, path); if (ret < 0) - btrfs_error(root->fs_info, ret, + btrfs_std_error(root->fs_info, ret, "Failed to delete chunk item."); out: btrfs_free_path(path); @@ -2806,7 +2806,7 @@ static int btrfs_relocate_chunk(struct btrfs_root *root, u64 chunk_offset) trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { ret = PTR_ERR(trans); - btrfs_std_error(root->fs_info, ret); + btrfs_std_error(root->fs_info, ret, NULL); return ret; } @@ -3461,7 +3461,7 @@ static void __cancel_balance(struct btrfs_fs_info *fs_info) unset_balance_control(fs_info); ret = del_balance_item(fs_info->tree_root); if (ret) - btrfs_std_error(fs_info, ret); + btrfs_std_error(fs_info, ret, NULL); atomic_set(&fs_info->mutually_exclusive_operation_running, 0); } From d74a625987a134b00749e3912bdb6d3ac83fd71d Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:56 +0800 Subject: [PATCH 09/30] Btrfs: use BTRFS_ERROR_DEV_MISSING_NOT_FOUND when missing device is not found Use btrfs specific error code BTRFS_ERROR_DEV_MISSING_NOT_FOUND instead of -ENOENT. Next this removes the logging when user specifies "missing" and we don't find it in the kernel device list. Logging are for system events not for user input errors. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 99f294cfdcc046..c3e2591dd74286 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2041,10 +2041,8 @@ int btrfs_find_device_missing_or_by_path(struct btrfs_root *root, } } - if (!*device) { - btrfs_err(root->fs_info, "no missing device found"); - return -ENOENT; - } + if (!*device) + return BTRFS_ERROR_DEV_MISSING_NOT_FOUND; return 0; } else { From 29c36d72535deb3d8961b3fb4b6a565190a6c63b Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:58 +0800 Subject: [PATCH 10/30] Btrfs: add btrfs_read_dev_one_super() to read one specific SB This uses a chunk of code from btrfs_read_dev_super() and creates a function called btrfs_read_dev_one_super() so that next patch can use it for scratch superblock. Signed-off-by: Anand Jain [renamed bufhead to bh] Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 53 +++++++++++++++++++++++++++++----------------- fs/btrfs/disk-io.h | 2 ++ 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 0f8e33f2bceabe..c7c739f420b521 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -3188,6 +3188,37 @@ static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate) put_bh(bh); } +int btrfs_read_dev_one_super(struct block_device *bdev, int copy_num, + struct buffer_head **bh_ret) +{ + struct buffer_head *bh; + struct btrfs_super_block *super; + u64 bytenr; + + bytenr = btrfs_sb_offset(copy_num); + if (bytenr + BTRFS_SUPER_INFO_SIZE >= i_size_read(bdev->bd_inode)) + return -EINVAL; + + bh = __bread(bdev, bytenr / 4096, BTRFS_SUPER_INFO_SIZE); + /* + * If we fail to read from the underlying devices, as of now + * the best option we have is to mark it EIO. + */ + if (!bh) + return -EIO; + + super = (struct btrfs_super_block *)bh->b_data; + if (btrfs_super_bytenr(super) != bytenr || + btrfs_super_magic(super) != BTRFS_MAGIC) { + brelse(bh); + return -EINVAL; + } + + *bh_ret = bh; + return 0; +} + + struct buffer_head *btrfs_read_dev_super(struct block_device *bdev) { struct buffer_head *bh; @@ -3195,7 +3226,6 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev) struct btrfs_super_block *super; int i; u64 transid = 0; - u64 bytenr; int ret = -EINVAL; /* we would like to check all the supers, but that would make @@ -3204,28 +3234,11 @@ struct buffer_head *btrfs_read_dev_super(struct block_device *bdev) * later supers, using BTRFS_SUPER_MIRROR_MAX instead */ for (i = 0; i < 1; i++) { - bytenr = btrfs_sb_offset(i); - if (bytenr + BTRFS_SUPER_INFO_SIZE >= - i_size_read(bdev->bd_inode)) - break; - bh = __bread(bdev, bytenr / 4096, - BTRFS_SUPER_INFO_SIZE); - /* - * If we fail to read from the underlying devices, as of now - * the best option we have is to mark it EIO. - */ - if (!bh) { - ret = -EIO; + ret = btrfs_read_dev_one_super(bdev, i, &bh); + if (ret) continue; - } super = (struct btrfs_super_block *)bh->b_data; - if (btrfs_super_bytenr(super) != bytenr || - btrfs_super_magic(super) != BTRFS_MAGIC) { - brelse(bh); - ret = -EINVAL; - continue; - } if (!latest || btrfs_super_generation(super) > transid) { brelse(latest); diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h index bdfb479ea85955..adeb31830b9cc1 100644 --- a/fs/btrfs/disk-io.h +++ b/fs/btrfs/disk-io.h @@ -60,6 +60,8 @@ void close_ctree(struct btrfs_root *root); int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root *root, int max_mirrors); struct buffer_head *btrfs_read_dev_super(struct block_device *bdev); +int btrfs_read_dev_one_super(struct block_device *bdev, int copy_num, + struct buffer_head **bh_ret); int btrfs_commit_super(struct btrfs_root *root); struct extent_buffer *btrfs_find_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr); From 12b1c2637b6e0763121648d637ffe603b422e986 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:59 +0800 Subject: [PATCH 11/30] Btrfs: enhance btrfs_scratch_superblock to scratch all superblocks This patch updates and renames btrfs_scratch_superblocks, (which is used by the replace device thread), with those fixes from the scratch superblock code section of btrfs_rm_device(). The fixes are: Scratch all copies of superblock Notify kobject that superblock has been changed Update time on the device So that btrfs_rm_device() can use the function btrfs_scratch_superblocks() instead of its own scratch code. And further replace deivce code which similarly releases device back to the system, will have the fixes from the btrfs device delete. Signed-off-by: Anand Jain [renamed to btrfs_scratch_superblock] Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 38 ++++++++++++++++++++++++++------------ fs/btrfs/volumes.h | 2 +- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index c3e2591dd74286..1cde7849e2dd74 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1924,7 +1924,8 @@ void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info, if (srcdev->writeable) { fs_devices->rw_devices--; /* zero out the old super if it is writable */ - btrfs_scratch_superblock(srcdev); + btrfs_scratch_superblocks(srcdev->bdev, + rcu_str_deref(srcdev->name)); } if (srcdev->bdev) @@ -1974,7 +1975,8 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, btrfs_sysfs_rm_device_link(fs_info->fs_devices, tgtdev); if (tgtdev->bdev) { - btrfs_scratch_superblock(tgtdev); + btrfs_scratch_superblocks(tgtdev->bdev, + rcu_str_deref(tgtdev->name)); fs_info->fs_devices->open_devices--; } fs_info->fs_devices->num_devices--; @@ -6738,22 +6740,34 @@ int btrfs_get_dev_stats(struct btrfs_root *root, return 0; } -int btrfs_scratch_superblock(struct btrfs_device *device) +void btrfs_scratch_superblocks(struct block_device *bdev, char *device_path) { struct buffer_head *bh; struct btrfs_super_block *disk_super; + int copy_num; - bh = btrfs_read_dev_super(device->bdev); - if (IS_ERR(bh)) - return PTR_ERR(bh); - disk_super = (struct btrfs_super_block *)bh->b_data; + if (!bdev) + return; - memset(&disk_super->magic, 0, sizeof(disk_super->magic)); - set_buffer_dirty(bh); - sync_dirty_buffer(bh); - brelse(bh); + for (copy_num = 0; copy_num < BTRFS_SUPER_MIRROR_MAX; + copy_num++) { - return 0; + if (btrfs_read_dev_one_super(bdev, copy_num, &bh)) + continue; + + disk_super = (struct btrfs_super_block *)bh->b_data; + + memset(&disk_super->magic, 0, sizeof(disk_super->magic)); + set_buffer_dirty(bh); + sync_dirty_buffer(bh); + brelse(bh); + } + + /* 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); } /* diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 413a70c66f4cff..8663bf7c50514c 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -474,7 +474,7 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, struct btrfs_device *tgtdev); void btrfs_init_dev_replace_tgtdev_for_resume(struct btrfs_fs_info *fs_info, struct btrfs_device *tgtdev); -int btrfs_scratch_superblock(struct btrfs_device *device); +void btrfs_scratch_superblocks(struct block_device *bdev, char *device_path); int btrfs_is_parity_mirror(struct btrfs_mapping_tree *map_tree, u64 logical, u64 len, int mirror_num); unsigned long btrfs_full_stripe_len(struct btrfs_root *root, From 9e271ae27e4407388c5b25c747abc25c07838b0b Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:33:02 +0800 Subject: [PATCH 12/30] Btrfs: kernel operation should come after user input has been verified By general rule of thumb there shouldn't be any way that user land could trigger a kernel operation just by sending wrong arguments. Here do commit cleanups after user input has been verified. Signed-off-by: Anand Jain Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 7aed8a8ce69476..02031fc07f7fe5 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -327,19 +327,6 @@ int btrfs_dev_replace_start(struct btrfs_root *root, args->start.tgtdev_name[0] == '\0') return -EINVAL; - /* - * Here we commit the transaction to make sure commit_total_bytes - * of all the devices are updated. - */ - trans = btrfs_attach_transaction(root); - if (!IS_ERR(trans)) { - ret = btrfs_commit_transaction(trans, root); - if (ret) - return ret; - } else if (PTR_ERR(trans) != -ENOENT) { - return PTR_ERR(trans); - } - /* the disk copy procedure reuses the scrub code */ mutex_lock(&fs_info->volume_mutex); ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid, @@ -356,6 +343,19 @@ int btrfs_dev_replace_start(struct btrfs_root *root, if (ret) return ret; + /* + * Here we commit the transaction to make sure commit_total_bytes + * of all the devices are updated. + */ + trans = btrfs_attach_transaction(root); + if (!IS_ERR(trans)) { + ret = btrfs_commit_transaction(trans, root); + if (ret) + return ret; + } else if (PTR_ERR(trans) != -ENOENT) { + return PTR_ERR(trans); + } + btrfs_dev_replace_lock(dev_replace); switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: From 097efc966ab3e2c3677698f4ab5b229bfff101dd Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:33:04 +0800 Subject: [PATCH 13/30] Btrfs: don't log error from btrfs_get_bdev_and_sb Originally the message was not in a helper but ended up there. We should print error messages from callers instead. Signed-off-by: Anand Jain [reworded subject and changelog] Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 1cde7849e2dd74..694d82bccb0b7d 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -198,7 +198,6 @@ btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags, void *holder, if (IS_ERR(*bdev)) { ret = PTR_ERR(*bdev); - printk(KERN_INFO "BTRFS: open %s failed\n", device_path); goto error; } From f190aa471a7a703444f6587ed79c24a04a22d848 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:33:05 +0800 Subject: [PATCH 14/30] Btrfs: add helper for closing one device Signed-off-by: Anand Jain [reworded subject and changelog] Signed-off-by: David Sterba --- fs/btrfs/volumes.c | 66 +++++++++++++++++++++++++--------------------- fs/btrfs/volumes.h | 1 + 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 694d82bccb0b7d..5688b7821b3dad 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -764,36 +764,7 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) mutex_lock(&fs_devices->device_list_mutex); list_for_each_entry_safe(device, tmp, &fs_devices->devices, dev_list) { - struct btrfs_device *new_device; - struct rcu_string *name; - - if (device->bdev) - fs_devices->open_devices--; - - if (device->writeable && - device->devid != BTRFS_DEV_REPLACE_DEVID) { - list_del_init(&device->dev_alloc_list); - fs_devices->rw_devices--; - } - - if (device->missing) - fs_devices->missing_devices--; - - new_device = btrfs_alloc_device(NULL, &device->devid, - device->uuid); - BUG_ON(IS_ERR(new_device)); /* -ENOMEM */ - - /* Safe because we are under uuid_mutex */ - if (device->name) { - name = rcu_string_strdup(device->name->str, GFP_NOFS); - BUG_ON(!name); /* -ENOMEM */ - rcu_assign_pointer(new_device->name, name); - } - - list_replace_rcu(&device->dev_list, &new_device->dev_list); - new_device->fs_devices = device->fs_devices; - - call_rcu(&device->rcu, free_device); + btrfs_close_one_device(device); } mutex_unlock(&fs_devices->device_list_mutex); @@ -6834,3 +6805,38 @@ void btrfs_reset_fs_info_ptr(struct btrfs_fs_info *fs_info) fs_devices = fs_devices->seed; } } + +void btrfs_close_one_device(struct btrfs_device *device) +{ + struct btrfs_fs_devices *fs_devices = device->fs_devices; + struct btrfs_device *new_device; + struct rcu_string *name; + + if (device->bdev) + fs_devices->open_devices--; + + if (device->writeable && + device->devid != BTRFS_DEV_REPLACE_DEVID) { + list_del_init(&device->dev_alloc_list); + fs_devices->rw_devices--; + } + + if (device->missing) + fs_devices->missing_devices--; + + new_device = btrfs_alloc_device(NULL, &device->devid, + device->uuid); + BUG_ON(IS_ERR(new_device)); /* -ENOMEM */ + + /* Safe because we are under uuid_mutex */ + if (device->name) { + name = rcu_string_strdup(device->name->str, GFP_NOFS); + BUG_ON(!name); /* -ENOMEM */ + rcu_assign_pointer(new_device->name, name); + } + + list_replace_rcu(&device->dev_list, &new_device->dev_list); + new_device->fs_devices = device->fs_devices; + + call_rcu(&device->rcu, free_device); +} diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 8663bf7c50514c..38450dc288379a 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -547,5 +547,6 @@ static inline void unlock_chunks(struct btrfs_root *root) struct list_head *btrfs_get_fs_uuids(void); void btrfs_set_fs_info_ptr(struct btrfs_fs_info *fs_info); void btrfs_reset_fs_info_ptr(struct btrfs_fs_info *fs_info); +void btrfs_close_one_device(struct btrfs_device *device); #endif From 73416dab235e5ff030e3b3c61da0cd6ced324fc9 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Fri, 14 Aug 2015 18:33:07 +0800 Subject: [PATCH 15/30] Btrfs: move kobj stuff out of dev_replace lock range To avoid deadlock described in commit 084b6e7c7607 ("btrfs: Fix a lockdep warning when running xfstest."), we should move kobj stuff out of dev_replace lock range. "It is because the btrfs_kobj_{add/rm}_device() will call memory allocation with GFP_KERNEL, which may flush fs page cache to free space, waiting for it self to do the commit, causing the deadlock. To solve the problem, move btrfs_kobj_{add/rm}_device() out of the dev_replace lock range, also involing split the btrfs_rm_dev_replace_srcdev() function into remove and free parts. Now only btrfs_rm_dev_replace_remove_srcdev() is called in dev_replace lock range, and kobj_{add/rm} and btrfs_rm_dev_replace_free_srcdev() are called out of the lock range." Signed-off-by: Liu Bo Signed-off-by: Anand Jain [added lockup description] Signed-off-by: David Sterba --- fs/btrfs/dev-replace.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 02031fc07f7fe5..0978e52ffa42cf 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -375,10 +375,6 @@ int btrfs_dev_replace_start(struct btrfs_root *root, WARN_ON(!tgt_device); dev_replace->tgtdev = tgt_device; - ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); - if (ret) - btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); - printk_in_rcu(KERN_INFO "BTRFS: dev_replace from %s (devid %llu) to %s started\n", src_device->missing ? "" : @@ -401,6 +397,10 @@ int btrfs_dev_replace_start(struct btrfs_root *root, args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; btrfs_dev_replace_unlock(dev_replace); + ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); + if (ret) + btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); + btrfs_wait_ordered_roots(root->fs_info, -1); /* force writing the updated state information to disk */ From 0aee7541fb72b69b90d9d14359679bcc7f80dc6d Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 2 Oct 2015 18:33:55 +0800 Subject: [PATCH 16/30] Btrfs: pass the error code to the btrfs_std_error and log ret This patch will log return value of add/del_qgroup_relation() and pass the err code of btrfs_run_qgroups to the btrfs_std_error(). Signed-off-by: Anand Jain --- fs/btrfs/ioctl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f704d1c79739aa..f43a104730187d 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4803,10 +4803,15 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg) sa->src, sa->dst); } + if (ret) + btrfs_err(root->fs_info, + "add/del qgroup relation failed, assign %llu ret %d", + sa->assign, ret); + /* update qgroup status and info */ err = btrfs_run_qgroups(trans, root->fs_info); if (err < 0) - btrfs_std_error(root->fs_info, ret, + btrfs_std_error(root->fs_info, err, "failed to update qgroup status and info\n"); err = btrfs_end_transaction(trans, root); if (err && !ret) From 8758f8727e229f6d235fb0f805346ee9e779eae9 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Sun, 20 Sep 2015 12:47:06 +0800 Subject: [PATCH 17/30] Btrfs: create a helper function to read the disk super A part of code from btrfs_scan_one_device() is moved to a new function btrfs_read_disk_super(), so that former function looks cleaner and moves the code to ensure null terminating label to it as well. Further there is opportunity to merge various duplicate code on read disk super. Earlier attempt on this was highlighted that there was some issues for which there are multiple versions, however it was not clear what was issue. So until its worked out we can keep it in a separate function. Signed-off-by: Anand Jain --- fs/btrfs/volumes.c | 87 +++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 5688b7821b3dad..dc2db9847d4979 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -906,6 +906,56 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, return ret; } +void btrfs_release_disk_super(struct page *page) +{ + kunmap(page); + page_cache_release(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_CACHE_SIZE >= i_size_read(bdev->bd_inode)) + return 1; + + /* make sure our super fits in the page */ + if (sizeof(**disk_super) > PAGE_CACHE_SIZE) + return 1; + + /* make sure our super doesn't straddle pages on disk */ + index = bytenr >> PAGE_CACHE_SHIFT; + if ((bytenr + sizeof(**disk_super) - 1) >> PAGE_CACHE_SHIFT != index) + return 1; + + /* 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)) + return 1; + + p = kmap(*page); + + /* align our pointer to the offset of the super block */ + *disk_super = p + (bytenr & ~PAGE_CACHE_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 * and we are not allowed to call set_blocksize during the scan. The superblock @@ -917,13 +967,11 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, struct btrfs_super_block *disk_super; struct block_device *bdev; struct page *page; - void *p; int ret = -EINVAL; u64 devid; u64 transid; u64 total_devices; u64 bytenr; - pgoff_t index; /* * we would like to check all the supers, but that would make @@ -936,41 +984,14 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, mutex_lock(&uuid_mutex); bdev = blkdev_get_by_path(path, flags, holder); - if (IS_ERR(bdev)) { ret = PTR_ERR(bdev); goto error; } - /* make sure our super fits in the device */ - if (bytenr + PAGE_CACHE_SIZE >= i_size_read(bdev->bd_inode)) + if (btrfs_read_disk_super(bdev, bytenr, &page, &disk_super)) goto error_bdev_put; - /* make sure our super fits in the page */ - if (sizeof(*disk_super) > PAGE_CACHE_SIZE) - goto error_bdev_put; - - /* make sure our super doesn't straddle pages on disk */ - index = bytenr >> PAGE_CACHE_SHIFT; - if ((bytenr + sizeof(*disk_super) - 1) >> PAGE_CACHE_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_CACHE_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); transid = btrfs_super_generation(disk_super); total_devices = btrfs_super_num_devices(disk_super); @@ -978,8 +999,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); if (ret > 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); } else { printk(KERN_INFO "BTRFS: device fsid %pU ", disk_super->fsid); @@ -991,9 +1010,7 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, if (!ret && fs_devices_ret) (*fs_devices_ret)->total_devices = total_devices; -error_unmap: - kunmap(page); - page_cache_release(page); + btrfs_release_disk_super(page); error_bdev_put: blkdev_put(bdev, flags); From a964face09bd8c906612f5aa7e26f964f7f23708 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 21 Sep 2015 15:04:33 +0800 Subject: [PATCH 18/30] btrfs: maintain consistency in logging to help debugging Optional Label may or may not be set, or it might be set at some time later. However while debugging to search through the kernel logs the scripts would need the logs to be consistent, so logs search key words shouldn't depend on the optional variables, instead fsid is better. Signed-off-by: Anand Jain --- fs/btrfs/volumes.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index dc2db9847d4979..af176d6fdacd83 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -998,13 +998,8 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, ret = device_list_add(path, disk_super, devid, fs_devices_ret); if (ret > 0) { - if (disk_super->label[0]) { - printk(KERN_INFO "BTRFS: device label %s ", disk_super->label); - } else { - printk(KERN_INFO "BTRFS: device fsid %pU ", disk_super->fsid); - } - - printk(KERN_CONT "devid %llu transid %llu %s\n", devid, transid, path); + printk(KERN_INFO "BTRFS: device fsid %pU devid %llu transid %llu %s\n", + disk_super->fsid, devid, transid, path); ret = 0; } if (!ret && fs_devices_ret) From defa97a6e01467d555520f15a78cd7dbbcc9d149 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 21 May 2015 13:21:11 +0800 Subject: [PATCH 19/30] Btrfs: device path change must be logged From the issue diagnosable point of view, log if the device path is changed. Signed-off-by: Anand Jain --- fs/btrfs/volumes.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index af176d6fdacd83..7880797adbda1f 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -504,6 +504,7 @@ void btrfs_free_stale_device(struct btrfs_device *cur_dev) * * Returns: * 1 - first time device is seen + * 2 - device already known but now is overwritten with new path * 0 - device already known * < 0 - error */ @@ -603,6 +604,7 @@ static noinline int device_list_add(const char *path, fs_devices->missing_devices--; device->missing = 0; } + ret = 2; } /* @@ -998,8 +1000,9 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, ret = device_list_add(path, disk_super, devid, fs_devices_ret); if (ret > 0) { - printk(KERN_INFO "BTRFS: device fsid %pU devid %llu transid %llu %s\n", - disk_super->fsid, devid, transid, path); + printk(KERN_INFO "BTRFS: device fsid %pU devid %llu transid %llu %s %s\n", + disk_super->fsid, devid, transid, path, + ret == 2 ? "(overwritten)":""); ret = 0; } if (!ret && fs_devices_ret) From e6aea8eea6214cce3994b12034b47ab81a11ad7b Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 5 Oct 2015 12:05:09 +0800 Subject: [PATCH 20/30] Btrfs: add missing brelse when superblock checksum fails looks like oversight, call brelse() when checksum fails. further down the code in the non error path we do call brelse() and so we don't see brelse() in the goto error.. paths. Signed-off-by: Anand Jain --- fs/btrfs/disk-io.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index c7c739f420b521..1b72cb599d8a64 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2665,6 +2665,7 @@ int open_ctree(struct super_block *sb, if (btrfs_check_super_csum(bh->b_data)) { printk(KERN_ERR "BTRFS: superblock checksum mismatch\n"); err = -EINVAL; + brelse(bh); goto fail_alloc; } From 0812a517c66026738520a3569f44c1f27671a299 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 5 Oct 2015 15:25:52 +0800 Subject: [PATCH 21/30] Btrfs: optimize check for stale device optimize check for stale device to only be checked when there is device added or changed. If there is no update to the device, there is no need to call btrfs_free_stale_device(). Signed-off-by: Anand Jain --- fs/btrfs/volumes.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 7880797adbda1f..893c0e84a2264a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -620,7 +620,8 @@ static noinline int device_list_add(const char *path, * if there is new btrfs on an already registered device, * then remove the stale device entry. */ - btrfs_free_stale_device(device); + if (ret > 0) + btrfs_free_stale_device(device); *fs_devices_ret = fs_devices; From bd74b0de5efee9c983a6f69926e558b190b3b381 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 18 Jun 2015 19:21:36 +0800 Subject: [PATCH 22/30] Btrfs: sysfs: support seed devices in the sysfs layout This adds an enhancement to show the seed fsid and its devices on the btrfs sysfs. The way sprouting handles fs_devices: clone seed fs_devices and add to the fs_uuids mem copy seed fs_devices and assign to fs_devices->seed (move dev_list) evacuate seed fs_devices contents to hold sprout fs devices contents So to be inline with this fs_devices changes during seeding, represent seed fsid under the sprout fsid, this is achieved by using the kobject_move() The end result will be, /sys/fs/btrfs/sprout-fsid/seed/level-1-seed-fsid/seed/(if)level-2-seed-fsid Signed-off-by: Anand Jain --- fs/btrfs/dev-replace.c | 7 +- fs/btrfs/disk-io.c | 6 +- fs/btrfs/sysfs.c | 150 ++++++++++++++++++++++++++++++++++++----- fs/btrfs/sysfs.h | 12 ++-- fs/btrfs/volumes.c | 26 +++---- fs/btrfs/volumes.h | 1 + 6 files changed, 160 insertions(+), 42 deletions(-) diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 0978e52ffa42cf..3a1a920c2972e4 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -397,7 +397,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root, args->result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; btrfs_dev_replace_unlock(dev_replace); - ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device); + ret = btrfs_sysfs_add_device_link(tgt_device->fs_devices, tgt_device, 0); if (ret) btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); @@ -586,7 +586,10 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, mutex_unlock(&uuid_mutex); /* replace the sysfs entry */ - btrfs_sysfs_rm_device_link(fs_info->fs_devices, src_device); + btrfs_sysfs_rm_device_link(fs_info->fs_devices, src_device, 0); + if (src_device->fs_devices->seeding && + !src_device->fs_devices->num_devices) + btrfs_sysfs_remove_fsid(src_device->fs_devices); btrfs_rm_dev_replace_free_srcdev(fs_info, src_device); /* write back the superblocks */ diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 1b72cb599d8a64..ac1e1680b85b07 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2922,13 +2922,13 @@ int open_ctree(struct super_block *sb, btrfs_close_extra_devices(fs_devices, 1); - ret = btrfs_sysfs_add_fsid(fs_devices, NULL); - if (ret) { + ret = btrfs_sysfs_add_fsid(fs_devices, NULL, 1); + if (ret && ret != -EEXIST) { pr_err("BTRFS: failed to init sysfs fsid interface: %d\n", ret); goto fail_block_groups; } - ret = btrfs_sysfs_add_device(fs_devices); + ret = btrfs_sysfs_add_device(fs_devices, 1); if (ret) { pr_err("BTRFS: failed to init sysfs device interface: %d\n", ret); goto fail_fsdev_sysfs; diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index e0ac85949067c3..3a4546280111be 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -517,6 +517,11 @@ static int addrm_unknown_feature_attrs(struct btrfs_fs_info *fs_info, bool add) static void __btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs) { + if (fs_devs->seed) { + __btrfs_sysfs_remove_fsid(fs_devs->seed); + btrfs_sysfs_rm_seed_dir(fs_devs); + } + if (fs_devs->device_dir_kobj) { kobject_del(fs_devs->device_dir_kobj); kobject_put(fs_devs->device_dir_kobj); @@ -557,7 +562,7 @@ void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info) addrm_unknown_feature_attrs(fs_info, false); sysfs_remove_group(&fs_info->fs_devices->fsid_kobj, &btrfs_feature_attr_group); sysfs_remove_files(&fs_info->fs_devices->fsid_kobj, btrfs_attrs); - btrfs_sysfs_rm_device_link(fs_info->fs_devices, NULL); + btrfs_sysfs_rm_device_link(fs_info->fs_devices, NULL, 1); } const char * const btrfs_feature_set_names[3] = { @@ -638,7 +643,7 @@ static void init_feature_attrs(void) /* when one_device is NULL, it removes all device links */ int btrfs_sysfs_rm_device_link(struct btrfs_fs_devices *fs_devices, - struct btrfs_device *one_device) + struct btrfs_device *one_device, int follow_seed) { struct hd_struct *disk; struct kobject *disk_kobj; @@ -668,27 +673,38 @@ int btrfs_sysfs_rm_device_link(struct btrfs_fs_devices *fs_devices, disk_kobj->name); } + if (follow_seed && fs_devices->seed) + btrfs_sysfs_rm_device_link(fs_devices->seed, NULL, follow_seed); + return 0; } -int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs) +int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs, int follow_seed) { - if (!fs_devs->device_dir_kobj) - fs_devs->device_dir_kobj = kobject_create_and_add("devices", - &fs_devs->fsid_kobj); + while (fs_devs) { + if (!fs_devs->device_dir_kobj) + fs_devs->device_dir_kobj = kobject_create_and_add( + "devices", &fs_devs->fsid_kobj); - if (!fs_devs->device_dir_kobj) - return -ENOMEM; + if (!fs_devs->device_dir_kobj) + return -ENOMEM; + + if (!follow_seed) + return 0; + + fs_devs = fs_devs->seed; + } return 0; } int btrfs_sysfs_add_device_link(struct btrfs_fs_devices *fs_devices, - struct btrfs_device *one_device) + struct btrfs_device *one_device, int follow_seed) { int error = 0; struct btrfs_device *dev; +again: list_for_each_entry(dev, &fs_devices->devices, dev_list) { struct hd_struct *disk; struct kobject *disk_kobj; @@ -708,9 +724,37 @@ int btrfs_sysfs_add_device_link(struct btrfs_fs_devices *fs_devices, break; } + if (follow_seed && fs_devices->seed) { + fs_devices = fs_devices->seed; + goto again; + } + return error; } +void btrfs_sysfs_rm_seed_dir(struct btrfs_fs_devices *fs_devs) +{ + if (fs_devs->seed_dir_kobj) { + kobject_del(fs_devs->seed_dir_kobj); + kobject_put(fs_devs->seed_dir_kobj); + fs_devs->seed_dir_kobj = NULL; + } +} + +int btrfs_sysfs_add_seed_dir(struct btrfs_fs_devices *fs_devs) +{ + if (!fs_devs->seed_dir_kobj) + fs_devs->seed_dir_kobj = kobject_create_and_add( + "seed", &fs_devs->fsid_kobj); + + if (!fs_devs->seed_dir_kobj) + return -ENOMEM; + + BUG_ON(!fs_devs->seed_dir_kobj->state_initialized); + + return 0; +} + /* /sys/fs/btrfs/ entry */ static struct kset *btrfs_kset; @@ -725,15 +769,29 @@ u64 btrfs_debugfs_test; * And parent can be specified for seed device */ int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, - struct kobject *parent) + struct kobject *parent, int follow_seed) { - int error; + int error = 0; - init_completion(&fs_devs->kobj_unregister); - fs_devs->fsid_kobj.kset = btrfs_kset; - error = kobject_init_and_add(&fs_devs->fsid_kobj, - &btrfs_ktype, parent, "%pU", fs_devs->fsid); - return error; +add_seed: + if (!fs_devs->fsid_kobj.state_initialized) { + init_completion(&fs_devs->kobj_unregister); + fs_devs->fsid_kobj.kset = btrfs_kset; + error = kobject_init_and_add(&fs_devs->fsid_kobj, + &btrfs_ktype, parent, "%pU", fs_devs->fsid); + } else { + error = -EEXIST; + } + + if (!follow_seed || !fs_devs->seed) + return error; + + btrfs_sysfs_add_seed_dir(fs_devs); + + parent = fs_devs->seed_dir_kobj; + fs_devs = fs_devs->seed; + + goto add_seed; } int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) @@ -744,13 +802,13 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) btrfs_set_fs_info_ptr(fs_info); - error = btrfs_sysfs_add_device_link(fs_devs, NULL); + error = btrfs_sysfs_add_device_link(fs_devs, NULL, 1); if (error) return error; error = sysfs_create_files(fsid_kobj, btrfs_attrs); if (error) { - btrfs_sysfs_rm_device_link(fs_devs, NULL); + btrfs_sysfs_rm_device_link(fs_devs, NULL, 0); return error; } @@ -826,3 +884,59 @@ void btrfs_exit_sysfs(void) debugfs_remove_recursive(btrfs_debugfs_root_dentry); } +void btrfs_sysfs_prepare_sprout_reset(void) +{ + /* close call would anyway cleanup */ +} + +void btrfs_sysfs_prepare_sprout(struct btrfs_fs_devices *fs_devices, + struct btrfs_fs_devices *seed_devices) +{ + char fsid_buf[BTRFS_UUID_UNPARSED_SIZE]; + + /* + * Sprouting has changed fsid of the mounted root, + * so rename the fsid on the sysfs + */ + snprintf(fsid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU", fs_devices->fsid); + if (kobject_rename(&fs_devices->fsid_kobj, fsid_buf)) { + pr_warn("Btrfs: sysfs: kobject rename failed\n"); + } + + /* + * Create the seed fsid inside the sprout fsid + * but should not create devices dir, instead + * move it from the original fs_devices + */ + memset(&seed_devices->fsid_kobj, 0, sizeof(struct kobject)); + seed_devices->device_dir_kobj = NULL; + memset(&seed_devices->kobj_unregister, 0, + sizeof(struct completion)); + seed_devices->seed_dir_kobj = NULL; + + if (!fs_devices->seed_dir_kobj) + btrfs_sysfs_add_seed_dir(fs_devices); + + btrfs_sysfs_add_fsid(seed_devices, fs_devices->seed_dir_kobj, 0); + + if (kobject_move(fs_devices->device_dir_kobj, + &seed_devices->fsid_kobj)) + pr_warn("Btrfs: sysfs: dev kobject move failed\n"); + + seed_devices->device_dir_kobj = fs_devices->device_dir_kobj; + fs_devices->device_dir_kobj = NULL; + btrfs_sysfs_add_device(fs_devices, 0); + + /* + * the kobj dev and devices attribute will be created + * in the main function as part of the init_new_device + * If this is a nested seed, that is if there is seed's + * seed device then move that one level deep. + */ + if (seed_devices->seed) { + btrfs_sysfs_add_seed_dir(seed_devices); + if (kobject_move(&seed_devices->seed->fsid_kobj, + seed_devices->seed_dir_kobj)) + pr_warn("Btrfs: sysfs: kobject move failed\n"); + } +} diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index 9c09522125a6b2..5ace2898ad511c 100644 --- a/fs/btrfs/sysfs.h +++ b/fs/btrfs/sysfs.h @@ -83,11 +83,15 @@ extern const char * const btrfs_feature_set_names[3]; extern struct kobj_type space_info_ktype; extern struct kobj_type btrfs_raid_ktype; int btrfs_sysfs_add_device_link(struct btrfs_fs_devices *fs_devices, - struct btrfs_device *one_device); + struct btrfs_device *one_device, int follow_seed); int btrfs_sysfs_rm_device_link(struct btrfs_fs_devices *fs_devices, - struct btrfs_device *one_device); + struct btrfs_device *one_device, int follow_seed); int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, - struct kobject *parent); -int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs); + struct kobject *parent, int follow_seed); void btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs); +int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs, int follow_seed); +int btrfs_sysfs_add_seed_dir(struct btrfs_fs_devices *fs_devs); +void btrfs_sysfs_rm_seed_dir(struct btrfs_fs_devices *fs_devs); +void btrfs_sysfs_prepare_sprout(struct btrfs_fs_devices *fs_devices, + struct btrfs_fs_devices *seed_devices); #endif /* _BTRFS_SYSFS_H_ */ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 893c0e84a2264a..227f4bea882465 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "ctree.h" #include "extent_map.h" @@ -1787,7 +1788,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) if (device->bdev) { device->fs_devices->open_devices--; /* remove sysfs entry */ - btrfs_sysfs_rm_device_link(root->fs_info->fs_devices, device); + btrfs_sysfs_rm_device_link(root->fs_info->fs_devices, device, 0); } call_rcu(&device->rcu, free_device); @@ -1958,7 +1959,7 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info, WARN_ON(!tgtdev); mutex_lock(&fs_info->fs_devices->device_list_mutex); - btrfs_sysfs_rm_device_link(fs_info->fs_devices, tgtdev); + btrfs_sysfs_rm_device_link(fs_info->fs_devices, tgtdev, 0); if (tgtdev->bdev) { btrfs_scratch_superblocks(tgtdev->bdev, @@ -2087,6 +2088,9 @@ static int btrfs_prepare_sprout(struct btrfs_root *root) fs_devices->open_devices = 0; fs_devices->missing_devices = 0; fs_devices->rotating = 0; + if (fs_devices->seed) + seed_devices->seed = fs_devices->seed; + fs_devices->seed = seed_devices; generate_random_uuid(fs_devices->fsid); @@ -2098,6 +2102,8 @@ static int btrfs_prepare_sprout(struct btrfs_root *root) ~BTRFS_SUPER_FLAG_SEEDING; btrfs_set_super_flags(disk_super, super_flags); + btrfs_sysfs_prepare_sprout(fs_devices, seed_devices); + return 0; } @@ -2295,7 +2301,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) tmp + 1); /* add sysfs device entry */ - btrfs_sysfs_add_device_link(root->fs_info->fs_devices, device); + btrfs_sysfs_add_device_link(root->fs_info->fs_devices, device, 0); /* * we've got more storage, clear any full flags on the space @@ -2323,22 +2329,11 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) } if (seeding_dev) { - char fsid_buf[BTRFS_UUID_UNPARSED_SIZE]; - ret = btrfs_finish_sprout(trans, root); if (ret) { btrfs_abort_transaction(trans, root, ret); goto error_trans; } - - /* Sprouting would change fsid of the mounted root, - * so rename the fsid on the sysfs - */ - snprintf(fsid_buf, BTRFS_UUID_UNPARSED_SIZE, "%pU", - root->fs_info->fsid); - if (kobject_rename(&root->fs_info->fs_devices->fsid_kobj, - fsid_buf)) - pr_warn("BTRFS: sysfs: failed to create fsid for sprout\n"); } root->fs_info->num_tolerated_disk_barrier_failures = @@ -2374,7 +2369,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) error_trans: btrfs_end_transaction(trans, root); rcu_string_free(device->name); - btrfs_sysfs_rm_device_link(root->fs_info->fs_devices, device); + btrfs_sysfs_rm_device_link(root->fs_info->fs_devices, device, 0); kfree(device); error: blkdev_put(bdev, FMODE_EXCL); @@ -6227,6 +6222,7 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_root *root, fs_devices->seed = root->fs_info->fs_devices->seed; root->fs_info->fs_devices->seed = fs_devices; + out: return fs_devices; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 38450dc288379a..a6e3b37cc6d965 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -246,6 +246,7 @@ struct btrfs_fs_devices { struct btrfs_fs_devices *seed; int seeding; + struct kobject *seed_dir_kobj; int opened; From b0b399067c0ac163dce2616c68ff306fec551f19 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 20 Mar 2015 18:01:42 +0800 Subject: [PATCH 23/30] Btrfs: create a framework to create pool attributes We need fsid kobject to hold pool attributes however its created only when fs is mounted. So, this patch changes the life cycle of the fsid and devices kobjects /sys/fs/btrfs/ and /sys/fs/btrfs//devices, from created and destroyed by mount and unmount event to created and destroyed by scanned and module-unload events respectively. However this does not alter life cycle of fs attributes as such. Signed-off-by: Anand Jain --- fs/btrfs/disk-io.c | 10 +++++----- fs/btrfs/sysfs.c | 7 ++++++- fs/btrfs/sysfs.h | 3 ++- fs/btrfs/volumes.c | 7 ++++++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index ac1e1680b85b07..ebeea0f03d0d1c 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2937,7 +2937,7 @@ int open_ctree(struct super_block *sb, ret = btrfs_sysfs_add_mounted(fs_info); if (ret) { pr_err("BTRFS: failed to init sysfs interface: %d\n", ret); - goto fail_fsdev_sysfs; + goto fail_block_groups; } ret = btrfs_init_space_info(fs_info); @@ -3116,9 +3116,6 @@ int open_ctree(struct super_block *sb, fail_sysfs: btrfs_sysfs_remove_mounted(fs_info); -fail_fsdev_sysfs: - btrfs_sysfs_remove_fsid(fs_info->fs_devices); - fail_block_groups: btrfs_put_block_group_cache(fs_info); btrfs_free_block_groups(fs_info); @@ -3815,7 +3812,10 @@ void close_ctree(struct btrfs_root *root) } btrfs_sysfs_remove_mounted(fs_info); - btrfs_sysfs_remove_fsid(fs_info->fs_devices); + if (fs_info->fs_devices->seed) { + btrfs_sysfs_remove_fsid(fs_info->fs_devices->seed); + btrfs_sysfs_rm_seed_dir(fs_info->fs_devices); + } btrfs_free_fs_roots(fs_info); diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index 3a4546280111be..c155d340c2a620 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -880,6 +880,7 @@ int btrfs_init_sysfs(void) void btrfs_exit_sysfs(void) { sysfs_remove_group(&btrfs_kset->kobj, &btrfs_feature_attr_group); + btrfs_sysfs_remove_fsid(NULL); kset_unregister(btrfs_kset); debugfs_remove_recursive(btrfs_debugfs_root_dentry); } @@ -890,7 +891,8 @@ void btrfs_sysfs_prepare_sprout_reset(void) } void btrfs_sysfs_prepare_sprout(struct btrfs_fs_devices *fs_devices, - struct btrfs_fs_devices *seed_devices) + struct btrfs_fs_devices *seed_devices, + struct btrfs_fs_devices *old_devices) { char fsid_buf[BTRFS_UUID_UNPARSED_SIZE]; @@ -939,4 +941,7 @@ void btrfs_sysfs_prepare_sprout(struct btrfs_fs_devices *fs_devices, seed_devices->seed_dir_kobj)) pr_warn("Btrfs: sysfs: kobject move failed\n"); } + + btrfs_sysfs_add_fsid(old_devices, NULL, 0); + btrfs_sysfs_add_device(old_devices, 0); } diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index 5ace2898ad511c..0a592f24e9cfe9 100644 --- a/fs/btrfs/sysfs.h +++ b/fs/btrfs/sysfs.h @@ -93,5 +93,6 @@ int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs, int follow_seed); int btrfs_sysfs_add_seed_dir(struct btrfs_fs_devices *fs_devs); void btrfs_sysfs_rm_seed_dir(struct btrfs_fs_devices *fs_devs); void btrfs_sysfs_prepare_sprout(struct btrfs_fs_devices *fs_devices, - struct btrfs_fs_devices *seed_devices); + struct btrfs_fs_devices *seed_devices, + struct btrfs_fs_devices *old_devices); #endif /* _BTRFS_SYSFS_H_ */ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 227f4bea882465..4af0cf662df997 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -528,6 +528,10 @@ static noinline int device_list_add(const char *path, list_add(&fs_devices->list, &fs_uuids); device = NULL; + if (btrfs_sysfs_add_fsid(fs_devices, NULL, 0)) + printk(KERN_WARNING "Btrfs: sysfs add fsid failed\n"); + if (btrfs_sysfs_add_device(fs_devices, 0)) + printk(KERN_WARNING "Btrfs: sysfs add device failed\n"); } else { device = __find_device(&fs_devices->devices, devid, disk_super->dev_item.uuid); @@ -797,6 +801,7 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) fs_devices = seed_devices; seed_devices = fs_devices->seed; __btrfs_close_devices(fs_devices); + btrfs_sysfs_remove_fsid(fs_devices); free_fs_devices(fs_devices); } /* @@ -2102,7 +2107,7 @@ static int btrfs_prepare_sprout(struct btrfs_root *root) ~BTRFS_SUPER_FLAG_SEEDING; btrfs_set_super_flags(disk_super, super_flags); - btrfs_sysfs_prepare_sprout(fs_devices, seed_devices); + btrfs_sysfs_prepare_sprout(fs_devices, seed_devices, old_devices); return 0; } From 0efce14bb163bd5eba09f5ffe967c17a7478faf7 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 23 Apr 2015 14:15:26 +0800 Subject: [PATCH 24/30] Btrfs: create helper function __check_raid_min_devices() move a section of btrfs_rm_device() code to check for min number of the devices into the function __check_raid_min_devices() v2: commit update and title renamed from Btrfs: move check for min number of devices to a function Signed-off-by: Anand Jain --- fs/btrfs/volumes.c | 51 +++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 4af0cf662df997..6be21ff1e89caf 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1629,23 +1629,20 @@ static int btrfs_rm_dev_item(struct btrfs_root *root, return ret; } -int btrfs_rm_device(struct btrfs_root *root, char *device_path) +static int __check_raid_min_devices(struct btrfs_root *root) { - struct btrfs_device *device; - struct btrfs_device *next_device; - struct block_device *bdev; - struct buffer_head *bh = NULL; - struct btrfs_super_block *disk_super; - struct btrfs_fs_devices *cur_devices; u64 all_avail; - u64 devid; u64 num_devices; - u8 *dev_uuid; unsigned seq; int ret = 0; - bool clear_super = false; - mutex_lock(&uuid_mutex); + num_devices = root->fs_info->fs_devices->num_devices; + btrfs_dev_replace_lock(&root->fs_info->dev_replace); + if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) { + WARN_ON(num_devices < 1); + num_devices--; + } + btrfs_dev_replace_unlock(&root->fs_info->dev_replace); do { seq = read_seqbegin(&root->fs_info->profiles_lock); @@ -1655,14 +1652,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) 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; - btrfs_dev_replace_lock(&root->fs_info->dev_replace); - if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) { - WARN_ON(num_devices < 1); - num_devices--; - } - btrfs_dev_replace_unlock(&root->fs_info->dev_replace); - if ((all_avail & BTRFS_BLOCK_GROUP_RAID10) && num_devices <= 4) { ret = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET; goto out; @@ -1684,6 +1673,30 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) goto out; } +out: + return ret; +} + +int btrfs_rm_device(struct btrfs_root *root, char *device_path) +{ + struct btrfs_device *device; + struct btrfs_device *next_device; + struct block_device *bdev; + struct buffer_head *bh = NULL; + struct btrfs_super_block *disk_super; + struct btrfs_fs_devices *cur_devices; + u64 devid; + u64 num_devices; + u8 *dev_uuid; + int ret = 0; + bool clear_super = false; + + mutex_lock(&uuid_mutex); + + ret = __check_raid_min_devices(root); + if (ret) + goto out; + if (strcmp(device_path, "missing") == 0) { struct list_head *devices; struct btrfs_device *tmp; From 3f7d4c2fb6c87ba95dde60542a2323118d5f909e Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 1 Oct 2015 21:38:02 +0800 Subject: [PATCH 25/30] Btrfs: clean up and optimize __check_raid_min_device() __check_raid_min_device() which was pealed from btrfs_rm_device() maintianed its original code to show the block move. This patch cleans up __check_raid_min_device(). Signed-off-by: Anand Jain --- fs/btrfs/volumes.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 6be21ff1e89caf..35ee3ca5aa2781 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1629,52 +1629,47 @@ static int btrfs_rm_dev_item(struct btrfs_root *root, return ret; } -static int __check_raid_min_devices(struct btrfs_root *root) +static int __check_raid_min_devices(struct btrfs_fs_info *fs_info) { u64 all_avail; u64 num_devices; unsigned seq; - int ret = 0; - num_devices = root->fs_info->fs_devices->num_devices; - btrfs_dev_replace_lock(&root->fs_info->dev_replace); - if (btrfs_dev_replace_is_ongoing(&root->fs_info->dev_replace)) { + num_devices = fs_info->fs_devices->num_devices; + btrfs_dev_replace_lock(&fs_info->dev_replace); + if (btrfs_dev_replace_is_ongoing(&fs_info->dev_replace)) { WARN_ON(num_devices < 1); num_devices--; } - btrfs_dev_replace_unlock(&root->fs_info->dev_replace); + btrfs_dev_replace_unlock(&fs_info->dev_replace); do { - seq = read_seqbegin(&root->fs_info->profiles_lock); + seq = read_seqbegin(&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)); + 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)); if ((all_avail & BTRFS_BLOCK_GROUP_RAID10) && num_devices <= 4) { - ret = BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET; - goto out; + return BTRFS_ERROR_DEV_RAID10_MIN_NOT_MET; } if ((all_avail & BTRFS_BLOCK_GROUP_RAID1) && num_devices <= 2) { - ret = BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET; - goto out; + return BTRFS_ERROR_DEV_RAID1_MIN_NOT_MET; } 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; + fs_info->fs_devices->rw_devices <= 2) { + return BTRFS_ERROR_DEV_RAID5_MIN_NOT_MET; } + if ((all_avail & BTRFS_BLOCK_GROUP_RAID6) && - root->fs_info->fs_devices->rw_devices <= 3) { - ret = BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET; - goto out; + fs_info->fs_devices->rw_devices <= 3) { + return BTRFS_ERROR_DEV_RAID6_MIN_NOT_MET; } -out: - return ret; + return 0; } int btrfs_rm_device(struct btrfs_root *root, char *device_path) @@ -1693,7 +1688,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) mutex_lock(&uuid_mutex); - ret = __check_raid_min_devices(root); + ret = __check_raid_min_devices(root->fs_info); if (ret) goto out; From 504bed358e3ae6c6cd37f310fb7cc58c767e0d2f Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 23 Apr 2015 09:38:56 +0800 Subject: [PATCH 26/30] Btrfs: create helper btrfs_find_device_by_user_input() The patch renames btrfs_dev_replace_find_srcdev() to btrfs_find_device_by_user_input() and moves it to volumes.c. so that delete device can use it. v2: changed title from 'Btrfs: create rename btrfs_dev_replace_find_srcdev()' and commit update Signed-off-by: Anand Jain --- fs/btrfs/dev-replace.c | 24 +----------------------- fs/btrfs/volumes.c | 19 +++++++++++++++++++ fs/btrfs/volumes.h | 3 +++ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 3a1a920c2972e4..c093f49ce6a426 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -44,9 +44,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( struct btrfs_fs_info *fs_info, struct btrfs_device *srcdev, struct btrfs_device *tgtdev); -static int btrfs_dev_replace_find_srcdev(struct btrfs_root *root, u64 srcdevid, - char *srcdev_name, - struct btrfs_device **device); static u64 __btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info); static int btrfs_dev_replace_kthread(void *data); static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info); @@ -329,7 +326,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root, /* the disk copy procedure reuses the scrub code */ mutex_lock(&fs_info->volume_mutex); - ret = btrfs_dev_replace_find_srcdev(root, args->start.srcdevid, + ret = btrfs_find_device_by_user_input(root, args->start.srcdevid, args->start.srcdev_name, &src_device); if (ret) { @@ -628,25 +625,6 @@ static void btrfs_dev_replace_update_device_in_mapping_tree( write_unlock(&em_tree->lock); } -static int btrfs_dev_replace_find_srcdev(struct btrfs_root *root, u64 srcdevid, - char *srcdev_name, - struct btrfs_device **device) -{ - int ret; - - if (srcdevid) { - ret = 0; - *device = btrfs_find_device(root->fs_info, srcdevid, NULL, - NULL); - if (!*device) - ret = -ENOENT; - } else { - ret = btrfs_find_device_missing_or_by_path(root, srcdev_name, - device); - } - return ret; -} - void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, struct btrfs_ioctl_dev_replace_args *args) { diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 35ee3ca5aa2781..9243f8e3b7ddf4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2052,6 +2052,25 @@ int btrfs_find_device_missing_or_by_path(struct btrfs_root *root, } } +int btrfs_find_device_by_user_input(struct btrfs_root *root, u64 srcdevid, + char *srcdev_name, + struct btrfs_device **device) +{ + int ret; + + if (srcdevid) { + ret = 0; + *device = btrfs_find_device(root->fs_info, srcdevid, NULL, + NULL); + if (!*device) + ret = -ENOENT; + } else { + ret = btrfs_find_device_missing_or_by_path(root, srcdev_name, + device); + } + return ret; +} + /* * does all the dirty work required for changing file system's UUID. */ diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index a6e3b37cc6d965..15ee0ddbbafdf9 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -430,6 +430,9 @@ void btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices, int step); int btrfs_find_device_missing_or_by_path(struct btrfs_root *root, char *device_path, struct btrfs_device **device); +int btrfs_find_device_by_user_input(struct btrfs_root *root, u64 srcdevid, + char *srcdev_name, + struct btrfs_device **device); struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, const u64 *devid, const u8 *uuid); From b737e10886eab1471afe9424fd96654a0a2883c6 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 23 Apr 2015 14:23:02 +0800 Subject: [PATCH 27/30] Btrfs: make use of btrfs_find_device_by_user_input() btrfs_rm_device() has a section of the code which can be replaced btrfs_find_device_by_user_input() Signed-off-by: Anand Jain --- fs/btrfs/volumes.c | 100 +++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 63 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 9243f8e3b7ddf4..fbbe3fe0d3dd17 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1676,13 +1676,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) { struct btrfs_device *device; struct btrfs_device *next_device; - struct block_device *bdev; + struct block_device *bdev = NULL; struct buffer_head *bh = NULL; - struct btrfs_super_block *disk_super; + struct btrfs_super_block *disk_super = NULL; struct btrfs_fs_devices *cur_devices; - u64 devid; u64 num_devices; - u8 *dev_uuid; int ret = 0; bool clear_super = false; @@ -1692,57 +1690,19 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) if (ret) 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; - } - } + ret = btrfs_find_device_by_user_input(root, 0, device_path, + &device); + if (ret) + goto out; if (device->is_tgtdev_for_dev_replace) { ret = BTRFS_ERROR_DEV_TGT_REPLACE; - goto error_brelse; + goto out; } if (device->writeable && root->fs_info->fs_devices->rw_devices == 1) { ret = BTRFS_ERROR_DEV_ONLY_WRITABLE; - goto error_brelse; + goto out; } if (device->writeable) { @@ -1832,16 +1792,33 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) * at this point, the device is zero sized. We want to * remove it from the devices list and zero out the old super */ - if (clear_super && disk_super) { + if (clear_super) { u64 bytenr; int i; + if (!disk_super) { + ret = btrfs_get_bdev_and_sb(device_path, + FMODE_WRITE | FMODE_EXCL, + root->fs_info->bdev_holder, 0, + &bdev, &bh); + if (ret) { + /* + * It could be a failed device ok for clear_super + * to fail. So return success + */ + ret = 0; + goto out; + } + + disk_super = (struct btrfs_super_block *)bh->b_data; + } /* 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); + brelse(bh); /* clear the mirror copies of super block on the disk * being removed, 0th copy is been taken care above and @@ -1853,7 +1830,6 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) i_size_read(bdev->bd_inode)) break; - brelse(bh); bh = __bread(bdev, bytenr / 4096, BTRFS_SUPER_INFO_SIZE); if (!bh) @@ -1863,32 +1839,30 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) if (btrfs_super_bytenr(disk_super) != bytenr || btrfs_super_magic(disk_super) != BTRFS_MAGIC) { + brelse(bh); continue; } memset(&disk_super->magic, 0, sizeof(disk_super->magic)); set_buffer_dirty(bh); sync_dirty_buffer(bh); + brelse(bh); } - } - ret = 0; - - if (bdev) { - /* Notify udev that device has changed */ - btrfs_kobject_uevent(bdev, KOBJ_CHANGE); + 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); + /* Update ctime/mtime for device path for libblkid */ + update_dev_time(device_path); + blkdev_put(bdev, FMODE_READ | FMODE_EXCL); + } } -error_brelse: - brelse(bh); - if (bdev) - blkdev_put(bdev, FMODE_READ | FMODE_EXCL); out: mutex_unlock(&uuid_mutex); return ret; + error_undo: if (device->writeable) { lock_chunks(root); @@ -1897,7 +1871,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) device->fs_devices->rw_devices++; unlock_chunks(root); } - goto error_brelse; + goto out; } void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info, From 427d0770444c2c1b629767a91804b615e2b31eb4 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 1 Jun 2015 00:12:38 +0800 Subject: [PATCH 28/30] Btrfs: enhance btrfs_find_device_by_user_input() to check device path The operation of device replace and device delete follows same steps upto some depth with in btrfs kernel, however they don't share codes. This enhancement will help replace and delete to share codes. Btrfs: enhance check device_path in btrfs_find_device_by_user_input() Signed-off-by: Anand Jain --- fs/btrfs/dev-replace.c | 4 ---- fs/btrfs/volumes.c | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index c093f49ce6a426..02df41943a1603 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -320,10 +320,6 @@ int btrfs_dev_replace_start(struct btrfs_root *root, return -EINVAL; } - if ((args->start.srcdevid == 0 && args->start.srcdev_name[0] == '\0') || - args->start.tgtdev_name[0] == '\0') - return -EINVAL; - /* the disk copy procedure reuses the scrub code */ mutex_lock(&fs_info->volume_mutex); ret = btrfs_find_device_by_user_input(root, args->start.srcdevid, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index fbbe3fe0d3dd17..e9292115ee031c 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2039,6 +2039,9 @@ int btrfs_find_device_by_user_input(struct btrfs_root *root, u64 srcdevid, if (!*device) ret = -ENOENT; } else { + if (!srcdev_name || !srcdev_name[0]) + return -EINVAL; + ret = btrfs_find_device_missing_or_by_path(root, srcdev_name, device); } From cef4787ed3d63d72d74ef5d1eecb6fead061a228 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 24 Apr 2015 09:40:08 +0800 Subject: [PATCH 29/30] Btrfs: make use of btrfs_scratch_superblocks() in btrfs_rm_device() With the previous patches now the btrfs_scratch_superblocks() is ready to be used in btrfs_rm_device() so use it. Signed-off-by: Anand Jain --- fs/btrfs/volumes.c | 78 +++++++++------------------------------------- 1 file changed, 14 insertions(+), 64 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index e9292115ee031c..69d311cb3f23ca 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1676,13 +1676,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) { struct btrfs_device *device; struct btrfs_device *next_device; - struct block_device *bdev = NULL; - struct buffer_head *bh = NULL; - struct btrfs_super_block *disk_super = NULL; struct btrfs_fs_devices *cur_devices; u64 num_devices; int ret = 0; bool clear_super = false; + char *dev_name = NULL; mutex_lock(&uuid_mutex); @@ -1710,6 +1708,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) list_del_init(&device->dev_alloc_list); device->fs_devices->rw_devices--; unlock_chunks(root); + dev_name = kstrdup(device->name->str, GFP_NOFS); + if (!dev_name) { + ret = -ENOMEM; + goto error_undo; + } clear_super = true; } @@ -1793,73 +1796,20 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) * remove it from the devices list and zero out the old super */ if (clear_super) { - u64 bytenr; - int i; - - if (!disk_super) { - ret = btrfs_get_bdev_and_sb(device_path, - FMODE_WRITE | FMODE_EXCL, - root->fs_info->bdev_holder, 0, - &bdev, &bh); - if (ret) { - /* - * It could be a failed device ok for clear_super - * to fail. So return success - */ - ret = 0; - goto out; - } - - disk_super = (struct btrfs_super_block *)bh->b_data; - } - /* 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); - brelse(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; - - bh = __bread(bdev, bytenr / 4096, - BTRFS_SUPER_INFO_SIZE); - if (!bh) - continue; + struct block_device *bdev; - disk_super = (struct btrfs_super_block *)bh->b_data; - - if (btrfs_super_bytenr(disk_super) != bytenr || - btrfs_super_magic(disk_super) != BTRFS_MAGIC) { - brelse(bh); - continue; - } - memset(&disk_super->magic, 0, - sizeof(disk_super->magic)); - set_buffer_dirty(bh); - sync_dirty_buffer(bh); - brelse(bh); - } - - 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); + 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); } } out: + if (dev_name) + kfree(dev_name); + mutex_unlock(&uuid_mutex); return ret; From 497198c375164385004208041ff7d00ffce29ac2 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 13 Apr 2015 21:50:01 +0800 Subject: [PATCH 30/30] Btrfs: Introduce device delete by devid This introduces new ioctl BTRFS_IOC_RM_DEV_V2, which uses enhanced struct btrfs_ioctl_vol_args_v2 to carry devid as an user argument. The patch won't delete the old ioctl interface and remains backward compatible with user land progs. Test case/script: echo "0 $(blockdev --getsz /dev/sdf) linear /dev/sdf 0" | dmsetup create bad_disk mkfs.btrfs -f -d raid1 -m raid1 /dev/sdd /dev/sde /dev/mapper/bad_disk mount /dev/sdd /btrfs dmsetup suspend bad_disk echo "0 $(blockdev --getsz /dev/sdf) error /dev/sdf 0" | dmsetup load bad_disk dmsetup resume bad_disk echo "bad disk failed. now deleting/replacing" btrfs dev del 3 /btrfs echo $? btrfs fi show /btrfs umount /btrfs btrfs-show-super /dev/sdd | egrep num_device dmsetup remove bad_disk wipefs -a /dev/sdf Signed-off-by: Anand Jain Reported-by: Martin --- fs/btrfs/ioctl.c | 58 +++++++++++++++++++++++++++++++++++++- fs/btrfs/volumes.c | 4 +-- fs/btrfs/volumes.h | 2 +- include/uapi/linux/btrfs.h | 14 ++++++++- 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f43a104730187d..f3aa37c1bc866e 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2653,6 +2653,60 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg) return ret; } +static long btrfs_ioctl_rm_dev_v2(struct file *file, void __user *arg) +{ + struct btrfs_root *root = BTRFS_I(file_inode(file))->root; + struct btrfs_ioctl_vol_args_v2 *vol_args; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + ret = mnt_want_write_file(file); + if (ret) + return ret; + + vol_args = memdup_user(arg, sizeof(*vol_args)); + if (IS_ERR(vol_args)) { + ret = PTR_ERR(vol_args); + goto err_drop; + } + + /* Check for compatibility reject unknown flags */ + if (vol_args->flags & ~BTRFS_VOL_ARG_V2_FLAGS) + return -ENOTTY; + + if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, + 1)) { + ret = BTRFS_ERROR_DEV_EXCL_RUN_IN_PROGRESS; + goto out; + } + + mutex_lock(&root->fs_info->volume_mutex); + if (vol_args->flags & BTRFS_DEVICE_BY_ID) { + ret = btrfs_rm_device(root, NULL, vol_args->devid); + } else { + vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; + ret = btrfs_rm_device(root, vol_args->name, 0); + } + + mutex_unlock(&root->fs_info->volume_mutex); + atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); + + if (!ret) { + if (vol_args->flags & BTRFS_DEVICE_BY_ID) + btrfs_info(root->fs_info, "disk devid %llu deleted", + vol_args->devid); + else + btrfs_info(root->fs_info, "disk deleted - %s", vol_args->name); + } +out: + kfree(vol_args); +err_drop: + mnt_drop_write_file(file); + return ret; +} + static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) { struct btrfs_root *root = BTRFS_I(file_inode(file))->root; @@ -2681,7 +2735,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg) } mutex_lock(&root->fs_info->volume_mutex); - ret = btrfs_rm_device(root, vol_args->name); + ret = btrfs_rm_device(root, vol_args->name, 0); mutex_unlock(&root->fs_info->volume_mutex); atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); @@ -5424,6 +5478,8 @@ long btrfs_ioctl(struct file *file, unsigned int return btrfs_ioctl_add_dev(root, argp); case BTRFS_IOC_RM_DEV: return btrfs_ioctl_rm_dev(file, argp); + case BTRFS_IOC_RM_DEV_V2: + return btrfs_ioctl_rm_dev_v2(file, argp); case BTRFS_IOC_FS_INFO: return btrfs_ioctl_fs_info(root, argp); case BTRFS_IOC_DEV_INFO: diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 69d311cb3f23ca..a384b65a99c56a 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1672,7 +1672,7 @@ static int __check_raid_min_devices(struct btrfs_fs_info *fs_info) return 0; } -int btrfs_rm_device(struct btrfs_root *root, char *device_path) +int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid) { struct btrfs_device *device; struct btrfs_device *next_device; @@ -1688,7 +1688,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) if (ret) goto out; - ret = btrfs_find_device_by_user_input(root, 0, device_path, + ret = btrfs_find_device_by_user_input(root, devid, device_path, &device); if (ret) goto out; diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 15ee0ddbbafdf9..4150d9d609e3b8 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -436,7 +436,7 @@ int btrfs_find_device_by_user_input(struct btrfs_root *root, u64 srcdevid, struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, const u64 *devid, const u8 *uuid); -int btrfs_rm_device(struct btrfs_root *root, char *device_path); +int btrfs_rm_device(struct btrfs_root *root, char *device_path, u64 devid); void btrfs_cleanup_fs_uuids(void); int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len); int btrfs_grow_device(struct btrfs_trans_handle *trans, diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index b6dec05c7196a2..ff40c48537b3cc 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -36,6 +36,13 @@ struct btrfs_ioctl_vol_args { #define BTRFS_SUBVOL_CREATE_ASYNC (1ULL << 0) #define BTRFS_SUBVOL_RDONLY (1ULL << 1) #define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2) +#define BTRFS_DEVICE_BY_ID (1ULL << 3) +#define BTRFS_VOL_ARG_V2_FLAGS \ + (BTRFS_SUBVOL_CREATE_ASYNC | \ + BTRFS_SUBVOL_RDONLY | \ + BTRFS_SUBVOL_QGROUP_INHERIT | \ + BTRFS_DEVICE_BY_ID) + #define BTRFS_FSID_SIZE 16 #define BTRFS_UUID_SIZE 16 #define BTRFS_UUID_UNPARSED_SIZE 37 @@ -76,7 +83,10 @@ struct btrfs_ioctl_vol_args_v2 { }; __u64 unused[4]; }; - char name[BTRFS_SUBVOL_NAME_MAX + 1]; + union { + char name[BTRFS_SUBVOL_NAME_MAX + 1]; + u64 devid; + }; }; /* @@ -634,5 +644,7 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code) struct btrfs_ioctl_feature_flags[2]) #define BTRFS_IOC_GET_SUPPORTED_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \ struct btrfs_ioctl_feature_flags[3]) +#define BTRFS_IOC_RM_DEV_V2 _IOW(BTRFS_IOCTL_MAGIC, 58, \ + struct btrfs_ioctl_vol_args_v2) #endif /* _UAPI_LINUX_BTRFS_H */