From 96f3136e51580ed68a2952341c8b9e2d7f853472 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 14 Aug 2015 18:32:46 +0800 Subject: [PATCH 01/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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 624779086214ead68f296ce647fe22d5b5958718 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 2 Oct 2015 18:33:55 +0800 Subject: [PATCH 16/29] 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 a1f170f67111b1e676f1f62a07e06881382d151d Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Sun, 20 Sep 2015 12:47:06 +0800 Subject: [PATCH 17/29] 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 dbf8590fab7240a249f7dddaf14e864730431523 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 21 Sep 2015 15:04:33 +0800 Subject: [PATCH 18/29] 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 adef2e262a360429f3999be07d44f34ee8c8d53e Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 21 May 2015 13:21:11 +0800 Subject: [PATCH 19/29] 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 f2c354c4171070cfc58fd5a113e24edbe8a2808a Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 5 Oct 2015 12:05:09 +0800 Subject: [PATCH 20/29] 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 26239dfd4e4940958aa8aefa66101f44eac64a85 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 18 Jun 2015 19:21:36 +0800 Subject: [PATCH 21/29] 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 7880797adbda1f..623f37df39d9f8 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" @@ -1786,7 +1787,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); @@ -1957,7 +1958,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, @@ -2086,6 +2087,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); @@ -2097,6 +2101,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; } @@ -2294,7 +2300,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 @@ -2322,22 +2328,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 = @@ -2373,7 +2368,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); @@ -6226,6 +6221,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 d7c35475f3a086c46c4efea51a193cd25a782e53 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 20 Mar 2015 18:01:42 +0800 Subject: [PATCH 22/29] 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 623f37df39d9f8..6aaa40906b2070 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); @@ -796,6 +800,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); } /* @@ -2101,7 +2106,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 08f4446b5381975cf8375c2faddeb667ad46428c Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 23 Apr 2015 14:15:26 +0800 Subject: [PATCH 23/29] 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 6aaa40906b2070..01e961dca3214f 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1628,23 +1628,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); @@ -1654,14 +1651,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; @@ -1683,6 +1672,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 e17012a815c1284de073bb6342baa06f66ab29ab Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 1 Oct 2015 21:38:02 +0800 Subject: [PATCH 24/29] 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 01e961dca3214f..476125e739e49b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1628,52 +1628,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) @@ -1692,7 +1687,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 cebed270bd0c28c63236552f17ab594334cbc6ee Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 23 Apr 2015 09:38:56 +0800 Subject: [PATCH 25/29] 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 476125e739e49b..c465546dd84b41 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2051,6 +2051,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 a871c3d53c9fdd81e3db82ea56c564e1d35c31be Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Thu, 23 Apr 2015 14:23:02 +0800 Subject: [PATCH 26/29] 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 c465546dd84b41..3a97e05428c857 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1675,13 +1675,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; @@ -1691,57 +1689,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) { @@ -1831,16 +1791,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 @@ -1852,7 +1829,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) @@ -1862,32 +1838,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); @@ -1896,7 +1870,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 1e5cd993dd7dabd7e6f6689a586c7872c11a1d59 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 1 Jun 2015 00:12:38 +0800 Subject: [PATCH 27/29] 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 3a97e05428c857..5251d97d80b6fb 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -2038,6 +2038,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 74b351a03efe57388ab95554ba782b48640000a2 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Fri, 24 Apr 2015 09:40:08 +0800 Subject: [PATCH 28/29] 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 5251d97d80b6fb..b4fa3e8071c0d0 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1675,13 +1675,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); @@ -1709,6 +1707,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; } @@ -1792,73 +1795,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 b4855932adb1b2d95957b56ed63889d890937697 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Mon, 24 Nov 2014 15:29:33 +0800 Subject: [PATCH 29/29] Btrfs: Introduce device pool sysfs attributes This patch makes btrfs_fs_devices and btrfs_device information readable from sysfs. This uses the sysfs group visible entry point to mark certain attributes visible/hidden depending the FS state. The new extended layout is as shown below. /sys/fs/btrfs/ ./7b047f4d-c2ce-4f22-94a3-68c09057f1bf* fsid* missing_devices num_devices* open_devices opened* rotating rw_devices seeding total_devices* total_rw_bytes ./e6701882-220a-4416-98ac-a99f095bddcc* active_pending bdev bytes_used can_discard devid* dev_root_fsid devstats_valid dev_totalbytes generation* in_fs_metadata io_align io_width missing name* nobarriers replace_tgtdev sector_size total_bytes type uuid* writeable (* indicates that attribute will be visible even when device is unmounted but registered with btrfs kernel) v2: use btrfs_error() not btrfs_err() reword subject form : Btrfs: add sysfs layout to show btrfs_fs_devices and btrfs_device attributes Signed-off-by: Anand Jain --- fs/btrfs/dev-replace.c | 6 + fs/btrfs/disk-io.c | 12 - fs/btrfs/sysfs.c | 521 ++++++++++++++++++++++++++++++++++++++--- fs/btrfs/sysfs.h | 12 +- fs/btrfs/volumes.c | 45 +++- fs/btrfs/volumes.h | 10 + 6 files changed, 563 insertions(+), 43 deletions(-) diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 02df41943a1603..7df4275c253157 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -394,6 +394,10 @@ int btrfs_dev_replace_start(struct btrfs_root *root, if (ret) btrfs_err(root->fs_info, "kobj add dev failed %d\n", ret); + ret = btrfs_sysfs_add_device_attr(tgt_device); + if (ret && ret != -EEXIST) + btrfs_err(root->fs_info, "sysfs create dev failed %d\n", ret); + btrfs_wait_ordered_roots(root->fs_info, -1); /* force writing the updated state information to disk */ @@ -583,6 +587,8 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info, if (src_device->fs_devices->seeding && !src_device->fs_devices->num_devices) btrfs_sysfs_remove_fsid(src_device->fs_devices); + else + btrfs_sysfs_rm_device_attr(src_device); 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 ebeea0f03d0d1c..e522773be85b1d 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -2922,18 +2922,6 @@ int open_ctree(struct super_block *sb, btrfs_close_extra_devices(fs_devices, 1); - 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, 1); - if (ret) { - pr_err("BTRFS: failed to init sysfs device interface: %d\n", ret); - goto fail_fsdev_sysfs; - } - ret = btrfs_sysfs_add_mounted(fs_info); if (ret) { pr_err("BTRFS: failed to init sysfs interface: %d\n", ret); diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index c155d340c2a620..d7f6aab05a98d3 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c @@ -31,9 +31,11 @@ #include "transaction.h" #include "sysfs.h" #include "volumes.h" +#include "rcu-string.h" static inline struct btrfs_fs_info *to_fs_info(struct kobject *kobj); static inline struct btrfs_fs_devices *to_fs_devs(struct kobject *kobj); +static inline struct btrfs_device *to_btrfs_dev(struct kobject *kobj); static u64 get_features(struct btrfs_fs_info *fs_info, enum btrfs_feature_set set) @@ -523,12 +525,14 @@ static void __btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs) } if (fs_devs->device_dir_kobj) { + btrfs_sysfs_rm_devices_attr(fs_devs); kobject_del(fs_devs->device_dir_kobj); kobject_put(fs_devs->device_dir_kobj); fs_devs->device_dir_kobj = NULL; } if (fs_devs->fsid_kobj.state_initialized) { + btrfs_sysfs_rm_fsid_attr(fs_devs); kobject_del(&fs_devs->fsid_kobj); kobject_put(&fs_devs->fsid_kobj); wait_for_completion(&fs_devs->kobj_unregister); @@ -563,6 +567,7 @@ void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info) 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, 1); + btrfs_sysfs_update_fsid_devices_attr(fs_info->fs_devices, 1); } const char * const btrfs_feature_set_names[3] = { @@ -679,21 +684,16 @@ int btrfs_sysfs_rm_device_link(struct btrfs_fs_devices *fs_devices, return 0; } -int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs, int follow_seed) +int btrfs_sysfs_add_device(struct btrfs_fs_devices *fs_devs) { - while (fs_devs) { - if (!fs_devs->device_dir_kobj) - fs_devs->device_dir_kobj = kobject_create_and_add( + 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 (!follow_seed) - return 0; + if (!fs_devs->device_dir_kobj) + return -ENOMEM; - fs_devs = fs_devs->seed; - } + BUG_ON(!fs_devs->device_dir_kobj->state_initialized); return 0; } @@ -769,29 +769,20 @@ 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, int follow_seed) + struct kobject *parent) { int error = 0; -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); + error = btrfs_sysfs_add_fsid_attr(fs_devs); } 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; + return error; } int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) @@ -812,6 +803,8 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info) return error; } + btrfs_sysfs_update_fsid_devices_attr(fs_devs, 1); + error = sysfs_create_group(fsid_kobj, &btrfs_feature_attr_group); if (error) @@ -919,7 +912,7 @@ void btrfs_sysfs_prepare_sprout(struct btrfs_fs_devices *fs_devices, 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); + btrfs_sysfs_add_fsid(seed_devices, fs_devices->seed_dir_kobj); if (kobject_move(fs_devices->device_dir_kobj, &seed_devices->fsid_kobj)) @@ -927,7 +920,7 @@ void btrfs_sysfs_prepare_sprout(struct btrfs_fs_devices *fs_devices, seed_devices->device_dir_kobj = fs_devices->device_dir_kobj; fs_devices->device_dir_kobj = NULL; - btrfs_sysfs_add_device(fs_devices, 0); + btrfs_sysfs_add_device(fs_devices); /* * the kobj dev and devices attribute will be created @@ -942,6 +935,480 @@ void btrfs_sysfs_prepare_sprout(struct btrfs_fs_devices *fs_devices, pr_warn("Btrfs: sysfs: kobject move failed\n"); } - btrfs_sysfs_add_fsid(old_devices, NULL, 0); - btrfs_sysfs_add_device(old_devices, 0); + btrfs_sysfs_add_fsid(old_devices, NULL); + btrfs_sysfs_add_device(old_devices); + btrfs_sysfs_add_devices_attr(old_devices); +} + + +static ssize_t btrfs_show_uuid(u8 *valptr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%pU\n", valptr); +} + +static ssize_t btrfs_show_str(char *strptr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", strptr); +} + +static ssize_t btrfs_show_u(uint val, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static ssize_t btrfs_show_d(int val, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + + +/* btrfs_fs_devices attributes */ +struct btrfs_fs_devs_attr { + struct kobj_attribute kobj_attr; +}; + +static ssize_t btrfs_fs_devs_attr_show(struct kobject *kobj, + struct kobj_attribute *a, char *buf); + +static ssize_t btrfs_fs_devs_attr_store(struct kobject *kobj, + struct kobj_attribute *a, + const char *buf, size_t count); + +#define BTRFS_FS_DEV_ATTR(_name)\ + static struct btrfs_fs_devs_attr btrfs_fs_devs_attr_##_name = {\ + .kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO,\ + btrfs_fs_devs_attr_show,\ + btrfs_fs_devs_attr_store),\ + } + +BTRFS_FS_DEV_ATTR(fsid); +BTRFS_FS_DEV_ATTR(num_devices); +BTRFS_FS_DEV_ATTR(open_devices); +BTRFS_FS_DEV_ATTR(rw_devices); +BTRFS_FS_DEV_ATTR(missing_devices); +BTRFS_FS_DEV_ATTR(total_rw_bytes); +BTRFS_FS_DEV_ATTR(total_devices); +BTRFS_FS_DEV_ATTR(opened); +BTRFS_FS_DEV_ATTR(seeding); +BTRFS_FS_DEV_ATTR(rotating); + +#define BTRFS_FS_DEV_ATTR_PTR(_name)\ + (&btrfs_fs_devs_attr_##_name.kobj_attr.attr) + +static struct attribute *btrfs_fs_devs_attrs[] = { + BTRFS_FS_DEV_ATTR_PTR(fsid), + BTRFS_FS_DEV_ATTR_PTR(num_devices), + BTRFS_FS_DEV_ATTR_PTR(open_devices), + BTRFS_FS_DEV_ATTR_PTR(rw_devices), + BTRFS_FS_DEV_ATTR_PTR(missing_devices), + BTRFS_FS_DEV_ATTR_PTR(total_rw_bytes), + BTRFS_FS_DEV_ATTR_PTR(total_devices), + BTRFS_FS_DEV_ATTR_PTR(opened), + BTRFS_FS_DEV_ATTR_PTR(seeding), + BTRFS_FS_DEV_ATTR_PTR(rotating), + NULL +}; + +#define BTRFS_FS_DEVS_GET_ATTR_UUID(attr, name, valprt, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_uuid(valprt, buf) +#define BTRFS_FS_DEVS_GET_ATTR_STR(attr, name, strprt, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_str(strprt, buf) +#define BTRFS_FS_DEVS_GET_ATTR_U64(attr, name, valprt, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_u64(valprt, NULL, buf) +#define BTRFS_FS_DEVS_GET_ATTR_U(attr, name, val, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_u(val, buf) +#define BTRFS_FS_DEVS_GET_ATTR_D(attr, name, val, buf)\ + if (attr == BTRFS_FS_DEV_ATTR_PTR(name))\ + return btrfs_show_d(val, buf) + +static ssize_t btrfs_fs_devs_attr_show(struct kobject *kobj, + struct kobj_attribute *a, char *buf) +{ + struct btrfs_fs_devices *fs_devs = to_fs_devs(kobj); + + BTRFS_FS_DEVS_GET_ATTR_UUID(&a->attr, fsid, fs_devs->fsid, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, num_devices, &fs_devs->num_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, open_devices, &fs_devs->open_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, rw_devices, &fs_devs->rw_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, missing_devices, + &fs_devs->missing_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, total_rw_bytes, + &fs_devs->total_rw_bytes, buf); + BTRFS_FS_DEVS_GET_ATTR_U64(&a->attr, total_devices, &fs_devs->total_devices, buf); + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, opened, fs_devs->opened, buf); + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, seeding, fs_devs->seeding, buf); + BTRFS_FS_DEVS_GET_ATTR_D(&a->attr, rotating, fs_devs->rotating, buf); + + return 0; +} + +static ssize_t btrfs_fs_devs_attr_store(struct kobject *kobj, + struct kobj_attribute *a, + const char *buf, size_t count) +{ + /* + * we might need some of the parameter to be writable + * but as of now just deny all + */ + return -EPERM; +} + + +static umode_t btrfs_sysfs_visible_fs_devs_attr(struct kobject *kobj, + struct attribute *attr, int unused) +{ + struct btrfs_fs_devices *fs_devs = to_fs_devs(kobj); + struct btrfs_fs_info *fs_info = fs_devs->fs_info; + + /* if device is mounted then all is visible */ + if (fs_devs->opened && fs_info && !fs_info->closing) + return attr->mode|S_IWUSR; + + /* when device is unmounted(ing) show only following set*/ + if (attr == BTRFS_FS_DEV_ATTR_PTR(num_devices)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_FS_DEV_ATTR_PTR(total_devices)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_FS_DEV_ATTR_PTR(opened)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_FS_DEV_ATTR_PTR(fsid)) + return attr->mode|S_IWUSR; + + return 0; +} + +static const struct attribute_group btrfs_fs_devs_attr_group = { + .attrs = btrfs_fs_devs_attrs, + .is_visible = btrfs_sysfs_visible_fs_devs_attr, +}; + +void btrfs_sysfs_rm_fsid_attr(struct btrfs_fs_devices *fs_devs) +{ + sysfs_remove_group(&fs_devs->fsid_kobj, + &btrfs_fs_devs_attr_group); +} + +int btrfs_sysfs_add_fsid_attr(struct btrfs_fs_devices *fs_devs) +{ + int rc; + + rc = sysfs_create_group(&fs_devs->fsid_kobj, + &btrfs_fs_devs_attr_group); + return rc; +} + +static int btrfs_sysfs_update_fsid_attr(struct btrfs_fs_devices *fs_devs) +{ + int rc; + + rc = sysfs_update_group(&fs_devs->fsid_kobj, + &btrfs_fs_devs_attr_group); + + return rc; +} + +/**** btrfs_device kobject and attributes ****/ +static ssize_t btrfs_dev_attr_show(struct kobject *kobj, + struct kobj_attribute *a, char *buf); +static ssize_t btrfs_dev_attr_store(struct kobject *kobj, + struct kobj_attribute *a, + const char *buf, size_t count); + +struct btrfs_dev_attr { + struct kobj_attribute kobj_attr; +}; + +static void btrfs_release_dev_kobj(struct kobject *kobj) +{ + struct btrfs_device *dev = to_btrfs_dev(kobj); + + kfree(dev->dev_kobjp); + dev->dev_kobjp = NULL; + complete(&dev->dev_kobj_unregister); +} + +static struct kobj_type btrfs_dev_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .release = btrfs_release_dev_kobj, +}; + +static inline struct btrfs_device *to_btrfs_dev(struct kobject *kobj) +{ + struct btrfs_device_kobj *dev_kobj; + + if (kobj->ktype != &btrfs_dev_ktype) + return NULL; + + dev_kobj = container_of(kobj, struct btrfs_device_kobj, dev_kobj); + return dev_kobj->device; +} + + +#define BTRFS_DEV_ATTR(_name)\ + static struct btrfs_dev_attr btrfs_dev_attr_##_name = {\ + .kobj_attr = __INIT_KOBJ_ATTR(_name, S_IRUGO,\ + btrfs_dev_attr_show,\ + btrfs_dev_attr_store),\ + } + +BTRFS_DEV_ATTR(uuid); +BTRFS_DEV_ATTR(name); +BTRFS_DEV_ATTR(devid); +BTRFS_DEV_ATTR(dev_root_fsid); +BTRFS_DEV_ATTR(generation); +BTRFS_DEV_ATTR(total_bytes); +BTRFS_DEV_ATTR(dev_totalbytes); +BTRFS_DEV_ATTR(bytes_used); +BTRFS_DEV_ATTR(type); +BTRFS_DEV_ATTR(io_align); +BTRFS_DEV_ATTR(io_width); +BTRFS_DEV_ATTR(sector_size); +BTRFS_DEV_ATTR(writeable); +BTRFS_DEV_ATTR(in_fs_metadata); +BTRFS_DEV_ATTR(missing); +BTRFS_DEV_ATTR(can_discard); +BTRFS_DEV_ATTR(replace_tgtdev); +BTRFS_DEV_ATTR(active_pending); +BTRFS_DEV_ATTR(nobarriers); +BTRFS_DEV_ATTR(devstats_valid); +BTRFS_DEV_ATTR(bdev); + +#define BTRFS_DEV_ATTR_PTR(_name)\ + (&btrfs_dev_attr_##_name.kobj_attr.attr) + +static struct attribute *btrfs_dev_attrs[] = { + BTRFS_DEV_ATTR_PTR(uuid), + BTRFS_DEV_ATTR_PTR(name), + BTRFS_DEV_ATTR_PTR(devid), + BTRFS_DEV_ATTR_PTR(dev_root_fsid), + BTRFS_DEV_ATTR_PTR(generation), + BTRFS_DEV_ATTR_PTR(total_bytes), + BTRFS_DEV_ATTR_PTR(dev_totalbytes), + BTRFS_DEV_ATTR_PTR(bytes_used), + BTRFS_DEV_ATTR_PTR(type), + BTRFS_DEV_ATTR_PTR(io_align), + BTRFS_DEV_ATTR_PTR(io_width), + BTRFS_DEV_ATTR_PTR(sector_size), + BTRFS_DEV_ATTR_PTR(writeable), + BTRFS_DEV_ATTR_PTR(in_fs_metadata), + BTRFS_DEV_ATTR_PTR(missing), + BTRFS_DEV_ATTR_PTR(can_discard), + BTRFS_DEV_ATTR_PTR(replace_tgtdev), + BTRFS_DEV_ATTR_PTR(active_pending), + BTRFS_DEV_ATTR_PTR(nobarriers), + BTRFS_DEV_ATTR_PTR(devstats_valid), + BTRFS_DEV_ATTR_PTR(bdev), + NULL +}; + +#define BTRFS_DEV_GET_ATTR_UUID(attr, name, valprt, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_uuid(valprt, buf) +#define BTRFS_DEV_GET_ATTR_STR(attr, name, strprt, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_str(strprt, buf) +#define BTRFS_DEV_GET_ATTR_U64(attr, name, valprt, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_u64(valprt, NULL, buf) +#define BTRFS_DEV_GET_ATTR_U(attr, name, val, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_u(val, buf) +#define BTRFS_DEV_GET_ATTR_D(attr, name, val, buf)\ + if (attr == BTRFS_DEV_ATTR_PTR(name))\ + return btrfs_show_d(val, buf) +#define BTRFS_DEV_CHECK_ATTR(attr, name)\ + attr == BTRFS_DEV_ATTR_PTR(name) + +static ssize_t btrfs_dev_attr_show(struct kobject *kobj, + struct kobj_attribute *a, char *buf) +{ + struct btrfs_device *dev = to_btrfs_dev(kobj); + char bdev_state[10]; + + /* Todo: handle the missing device case */ + BTRFS_DEV_GET_ATTR_STR(&a->attr, name, rcu_str_deref(dev->name), buf); + BTRFS_DEV_GET_ATTR_UUID(&a->attr, uuid, dev->uuid, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, devid, &dev->devid, buf); + BTRFS_DEV_GET_ATTR_UUID(&a->attr, dev_root_fsid, + dev->dev_root->fs_info->fsid, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, generation, &dev->generation, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, total_bytes, &dev->total_bytes, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, dev_totalbytes, &dev->disk_total_bytes, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, bytes_used, &dev->bytes_used, buf); + BTRFS_DEV_GET_ATTR_U64(&a->attr, type, &dev->type, buf); + BTRFS_DEV_GET_ATTR_U(&a->attr, io_align, dev->io_align, buf); + BTRFS_DEV_GET_ATTR_U(&a->attr, sector_size, dev->sector_size, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, writeable, dev->writeable, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, in_fs_metadata, dev->in_fs_metadata, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, missing, dev->missing, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, can_discard, dev->can_discard, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, replace_tgtdev, + dev->is_tgtdev_for_dev_replace, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, active_pending, dev->running_pending, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, nobarriers, dev->nobarriers, buf); + BTRFS_DEV_GET_ATTR_D(&a->attr, devstats_valid, dev->dev_stats_valid, buf); + if (dev->bdev) + strcpy(bdev_state, "not_null"); + else + strcpy(bdev_state, "null"); + BTRFS_DEV_GET_ATTR_STR(&a->attr, bdev, bdev_state, buf); + + return 0; +} + +static ssize_t btrfs_dev_attr_store(struct kobject *kobj, + struct kobj_attribute *a, + const char *buf, size_t count) +{ + /* + * we might need some of the parameter to be writable + * but as of now just deny all + */ + return -EPERM; +} + +static umode_t btrfs_sysfs_visible_dev_attr(struct kobject *kobj, + struct attribute *attr, int unused) +{ + struct btrfs_fs_devices *fs_devs; + struct btrfs_fs_info *fs_info; + + fs_devs = to_btrfs_dev(kobj)->fs_devices; + if (!fs_devs) { + BUG_ON(fs_devs == NULL); + return 0; + } + fs_info = fs_devs->fs_info; + + /* if device is mounted then all is visible */ + if (fs_devs->opened && fs_info && !fs_info->closing) + return attr->mode|S_IWUSR; + + /* when device is unmounted only the below attributes are visible */ + if (attr == BTRFS_DEV_ATTR_PTR(uuid)) + return attr->mode|S_IWUSR; + if (attr == BTRFS_DEV_ATTR_PTR(name)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_DEV_ATTR_PTR(devid)) + return attr->mode|S_IWUSR; + else if (attr == BTRFS_DEV_ATTR_PTR(generation)) + return attr->mode|S_IWUSR; + + return 0; +} + +static const struct attribute_group btrfs_dev_attr_group = { + .attrs = btrfs_dev_attrs, + .is_visible = btrfs_sysfs_visible_dev_attr, +}; + +void btrfs_sysfs_rm_device_attr(struct btrfs_device *dev) +{ + if (dev->dev_kobjp) { + struct kobject *kobj = &dev->dev_kobjp->dev_kobj; + + if (kobj->state_initialized) { + sysfs_remove_group(kobj, &btrfs_dev_attr_group); + kobject_del(kobj); + kobject_put(kobj); + wait_for_completion(&dev->dev_kobj_unregister); + return; + } + } + pr_warn("Btrfs: sysfs: dev destroy called for non init kobj\n"); + return; +} + +void btrfs_sysfs_rm_devices_attr(struct btrfs_fs_devices *fs_devs) +{ + struct btrfs_device *dev; + + list_for_each_entry(dev, &fs_devs->devices, dev_list) { + btrfs_sysfs_rm_device_attr(dev); + } +} + +int btrfs_sysfs_add_device_attr(struct btrfs_device *dev) +{ + int rc; + struct kobject *kobj; + + if (!dev->dev_kobjp) + dev->dev_kobjp = kzalloc(sizeof(struct btrfs_device_kobj), + GFP_NOFS); + else + return -EEXIST; + + if (!dev->dev_kobjp) + return -ENOMEM; + + dev->dev_kobjp->device = dev; + kobj = &dev->dev_kobjp->dev_kobj; + + init_completion(&dev->dev_kobj_unregister); + + rc = kobject_init_and_add(kobj, &btrfs_dev_ktype, + dev->fs_devices->device_dir_kobj, "%pU", dev->uuid); + if (!rc) + rc = sysfs_create_group(kobj, &btrfs_dev_attr_group); + + return rc; +} + +void btrfs_sysfs_add_devices_attr(struct btrfs_fs_devices *fs_devs) +{ + struct btrfs_device *dev; + + list_for_each_entry(dev, &fs_devs->devices, dev_list) { + if (btrfs_sysfs_add_device_attr(dev)) + printk(KERN_WARNING "BTRFS: create dev sysfs failed\n"); + } +} + +static int btrfs_sysfs_update_device_attr(struct btrfs_device *dev) +{ + struct kobject *kobj = &dev->dev_kobjp->dev_kobj; + + if (!kobj) + return -EINVAL; + + return sysfs_update_group(kobj, &btrfs_dev_attr_group); +} + +static int btrfs_sysfs_update_devices_attr(struct btrfs_fs_devices *fs_devs) +{ + int rc; + struct btrfs_device *dev; + + list_for_each_entry(dev, &fs_devs->devices, dev_list) { + if (!dev->dev_kobjp) + continue; + rc = btrfs_sysfs_update_device_attr(dev); + if (rc) { + pr_warn("BTRFS: update dev sysfs failed\n"); + return rc; + } + } + return 0; +} + +int btrfs_sysfs_update_fsid_devices_attr(struct btrfs_fs_devices *fs_devs, + int follow_seed) +{ + int rc; + +again_for_seeds: + rc = btrfs_sysfs_update_fsid_attr(fs_devs); + rc = btrfs_sysfs_update_devices_attr(fs_devs); + + if (follow_seed && fs_devs->seed) { + fs_devs = fs_devs->seed; + goto again_for_seeds; + } + + return rc; } diff --git a/fs/btrfs/sysfs.h b/fs/btrfs/sysfs.h index 0a592f24e9cfe9..29e96b9c5bbeeb 100644 --- a/fs/btrfs/sysfs.h +++ b/fs/btrfs/sysfs.h @@ -87,12 +87,20 @@ int btrfs_sysfs_add_device_link(struct btrfs_fs_devices *fs_devices, int btrfs_sysfs_rm_device_link(struct btrfs_fs_devices *fs_devices, struct btrfs_device *one_device, int follow_seed); int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs, - struct kobject *parent, int follow_seed); + struct kobject *parent); 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_device(struct btrfs_fs_devices *fs_devs); 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 *old_devices); +int btrfs_sysfs_add_device_attr(struct btrfs_device *dev); +void btrfs_sysfs_rm_device_attr(struct btrfs_device *dev); +void btrfs_sysfs_add_devices_attr(struct btrfs_fs_devices *fs_devs); +void btrfs_sysfs_rm_devices_attr(struct btrfs_fs_devices *fs_devs); +int btrfs_sysfs_add_fsid_attr(struct btrfs_fs_devices *fs_devs); +void btrfs_sysfs_rm_fsid_attr(struct btrfs_fs_devices *fs_devs); +int btrfs_sysfs_update_fsid_devices_attr(struct btrfs_fs_devices *fs_devs, + int follow_seed); #endif /* _BTRFS_SYSFS_H_ */ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index b4fa3e8071c0d0..ffe1dc944bfcbe 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -491,6 +491,7 @@ void btrfs_free_stale_device(struct btrfs_device *cur_dev) free_fs_devices(fs_devs); } else { fs_devs->num_devices--; + btrfs_sysfs_rm_device_attr(dev); list_del(&dev->dev_list); rcu_string_free(dev->name); kfree(dev); @@ -528,9 +529,9 @@ 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)) + if (btrfs_sysfs_add_fsid(fs_devices, NULL)) printk(KERN_WARNING "Btrfs: sysfs add fsid failed\n"); - if (btrfs_sysfs_add_device(fs_devices, 0)) + if (btrfs_sysfs_add_device(fs_devices)) printk(KERN_WARNING "Btrfs: sysfs add device failed\n"); } else { device = __find_device(&fs_devices->devices, devid, @@ -562,6 +563,10 @@ static noinline int device_list_add(const char *path, ret = 1; device->fs_devices = fs_devices; + + if (btrfs_sysfs_add_device_attr(device)) + pr_warn("Btrfs: sysfs create dev failed\n"); + } else if (!device->name || strcmp(device->name->str, path)) { /* * When FS is already mounted. @@ -1765,6 +1770,7 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) /* remove sysfs entry */ btrfs_sysfs_rm_device_link(root->fs_info->fs_devices, device, 0); } + btrfs_sysfs_rm_device_attr(device); call_rcu(&device->rcu, free_device); @@ -2260,6 +2266,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) /* add sysfs device entry */ btrfs_sysfs_add_device_link(root->fs_info->fs_devices, device, 0); + btrfs_sysfs_add_device_attr(device); /* * we've got more storage, clear any full flags on the space @@ -5961,6 +5968,8 @@ static struct btrfs_device *add_missing_dev(struct btrfs_root *root, device->missing = 1; fs_devices->missing_devices++; + btrfs_sysfs_add_device_attr(device); + return device; } @@ -6181,6 +6190,31 @@ 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; + if (!root->fs_info->fs_devices->fsid_kobj.state_initialized) { + pr_err("BTRFS: sysfs base fsid uninitialized\n"); + goto out; + } + + btrfs_sysfs_add_seed_dir(root->fs_info->fs_devices); + ret = btrfs_sysfs_add_fsid(fs_devices, + root->fs_info->fs_devices->seed_dir_kobj); + if (!ret || ret == -EEXIST) { + if (fs_devices->seed) { + btrfs_sysfs_add_seed_dir(fs_devices); + if (kobject_move(&fs_devices->seed->fsid_kobj, + fs_devices->seed_dir_kobj)) + pr_warn("Btrfs: sysfs: kobj move failed for seed\n"); + } + + ret = btrfs_sysfs_add_device(fs_devices); + if (ret) + pr_err("BTRFS: failed to init sysfs device dir: %d\n", ret); + + btrfs_sysfs_add_devices_attr(fs_devices); + } else { + pr_err("BTRFS: failed to init sysfs fsid: %d\n", ret); + } + out: return fs_devices; } @@ -6808,5 +6842,12 @@ void btrfs_close_one_device(struct btrfs_device *device) list_replace_rcu(&device->dev_list, &new_device->dev_list); new_device->fs_devices = device->fs_devices; + if (device->dev_kobjp) { + new_device->dev_kobjp = device->dev_kobjp; + new_device->dev_kobjp->device = new_device; + device->dev_kobjp = NULL; + } + init_completion(&new_device->dev_kobj_unregister); + call_rcu(&device->rcu, free_device); } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 15ee0ddbbafdf9..272cf220d6f3ef 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -47,6 +47,11 @@ struct btrfs_pending_bios { #define btrfs_device_data_ordered_init(device) do { } while (0) #endif +struct btrfs_device_kobj { + struct kobject dev_kobj; + struct btrfs_device *device; +}; + struct btrfs_device { struct list_head dev_list; struct list_head dev_alloc_list; @@ -150,6 +155,9 @@ struct btrfs_device { /* Counter to record the change of device stats */ atomic_t dev_stats_ccnt; atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX]; + + struct btrfs_device_kobj *dev_kobjp; + struct completion dev_kobj_unregister; }; /* @@ -260,6 +268,8 @@ struct btrfs_fs_devices { struct kobject fsid_kobj; struct kobject *device_dir_kobj; struct completion kobj_unregister; + + long unsigned int state; }; #define BTRFS_BIO_INLINE_CSUM_SIZE 64