diff options
author | Jeff Mahoney <jeffm@suse.com> | 2014-05-27 12:59:57 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-06-09 20:21:01 -0400 |
commit | c1895442be01c58449e3bf9272f22062a670e08f (patch) | |
tree | 8a09bb69c59dfdbcff5869cb82490487ec0bf86e /fs/btrfs | |
parent | 7e3ae33efad1490d01040f552ef50e58ed6376ca (diff) |
btrfs: allocate raid type kobjects dynamically
We are currently allocating space_info objects in an array when we
allocate space_info. When a user does something like:
# btrfs balance start -mconvert=raid1 -dconvert=raid1 /mnt
# btrfs balance start -mconvert=single -dconvert=single /mnt -f
# btrfs balance start -mconvert=raid1 -dconvert=raid1 /
We can end up with memory corruption since the kobject hasn't
been reinitialized properly and the name pointer was left set.
The rationale behind allocating them statically was to avoid
creating a separate kobject container that just contained the
raid type. It used the index in the array to determine the index.
Ultimately, though, this wastes more memory than it saves in all
but the most complex scenarios and introduces kobject lifetime
questions.
This patch allocates the kobjects dynamically instead. Note that
we also remove the kobject_get/put of the parent kobject since
kobject_add and kobject_del do that internally.
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
Reported-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ctree.h | 8 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 39 | ||||
-rw-r--r-- | fs/btrfs/sysfs.c | 5 |
3 files changed, 36 insertions, 16 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 02895a126ab9..af523d695432 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1123,6 +1123,12 @@ struct btrfs_qgroup_limit_item { | |||
1123 | __le64 rsv_excl; | 1123 | __le64 rsv_excl; |
1124 | } __attribute__ ((__packed__)); | 1124 | } __attribute__ ((__packed__)); |
1125 | 1125 | ||
1126 | /* For raid type sysfs entries */ | ||
1127 | struct raid_kobject { | ||
1128 | int raid_type; | ||
1129 | struct kobject kobj; | ||
1130 | }; | ||
1131 | |||
1126 | struct btrfs_space_info { | 1132 | struct btrfs_space_info { |
1127 | spinlock_t lock; | 1133 | spinlock_t lock; |
1128 | 1134 | ||
@@ -1173,7 +1179,7 @@ struct btrfs_space_info { | |||
1173 | wait_queue_head_t wait; | 1179 | wait_queue_head_t wait; |
1174 | 1180 | ||
1175 | struct kobject kobj; | 1181 | struct kobject kobj; |
1176 | struct kobject block_group_kobjs[BTRFS_NR_RAID_TYPES]; | 1182 | struct kobject *block_group_kobjs[BTRFS_NR_RAID_TYPES]; |
1177 | }; | 1183 | }; |
1178 | 1184 | ||
1179 | #define BTRFS_BLOCK_RSV_GLOBAL 1 | 1185 | #define BTRFS_BLOCK_RSV_GLOBAL 1 |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 6caddd5970e4..fafb3e53ecde 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -3497,10 +3497,8 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, | |||
3497 | return ret; | 3497 | return ret; |
3498 | } | 3498 | } |
3499 | 3499 | ||
3500 | for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) { | 3500 | for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) |
3501 | INIT_LIST_HEAD(&found->block_groups[i]); | 3501 | INIT_LIST_HEAD(&found->block_groups[i]); |
3502 | kobject_init(&found->block_group_kobjs[i], &btrfs_raid_ktype); | ||
3503 | } | ||
3504 | init_rwsem(&found->groups_sem); | 3502 | init_rwsem(&found->groups_sem); |
3505 | spin_lock_init(&found->lock); | 3503 | spin_lock_init(&found->lock); |
3506 | found->flags = flags & BTRFS_BLOCK_GROUP_TYPE_MASK; | 3504 | found->flags = flags & BTRFS_BLOCK_GROUP_TYPE_MASK; |
@@ -8586,8 +8584,9 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info) | |||
8586 | list_del(&space_info->list); | 8584 | list_del(&space_info->list); |
8587 | for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) { | 8585 | for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) { |
8588 | struct kobject *kobj; | 8586 | struct kobject *kobj; |
8589 | kobj = &space_info->block_group_kobjs[i]; | 8587 | kobj = space_info->block_group_kobjs[i]; |
8590 | if (kobj->parent) { | 8588 | space_info->block_group_kobjs[i] = NULL; |
8589 | if (kobj) { | ||
8591 | kobject_del(kobj); | 8590 | kobject_del(kobj); |
8592 | kobject_put(kobj); | 8591 | kobject_put(kobj); |
8593 | } | 8592 | } |
@@ -8611,17 +8610,26 @@ static void __link_block_group(struct btrfs_space_info *space_info, | |||
8611 | up_write(&space_info->groups_sem); | 8610 | up_write(&space_info->groups_sem); |
8612 | 8611 | ||
8613 | if (first) { | 8612 | if (first) { |
8614 | struct kobject *kobj = &space_info->block_group_kobjs[index]; | 8613 | struct raid_kobject *rkobj; |
8615 | int ret; | 8614 | int ret; |
8616 | 8615 | ||
8617 | kobject_get(&space_info->kobj); /* put in release */ | 8616 | rkobj = kzalloc(sizeof(*rkobj), GFP_NOFS); |
8618 | ret = kobject_add(kobj, &space_info->kobj, "%s", | 8617 | if (!rkobj) |
8619 | get_raid_name(index)); | 8618 | goto out_err; |
8619 | rkobj->raid_type = index; | ||
8620 | kobject_init(&rkobj->kobj, &btrfs_raid_ktype); | ||
8621 | ret = kobject_add(&rkobj->kobj, &space_info->kobj, | ||
8622 | "%s", get_raid_name(index)); | ||
8620 | if (ret) { | 8623 | if (ret) { |
8621 | pr_warn("BTRFS: failed to add kobject for block cache. ignoring.\n"); | 8624 | kobject_put(&rkobj->kobj); |
8622 | kobject_put(&space_info->kobj); | 8625 | goto out_err; |
8623 | } | 8626 | } |
8627 | space_info->block_group_kobjs[index] = &rkobj->kobj; | ||
8624 | } | 8628 | } |
8629 | |||
8630 | return; | ||
8631 | out_err: | ||
8632 | pr_warn("BTRFS: failed to add kobject for block cache. ignoring.\n"); | ||
8625 | } | 8633 | } |
8626 | 8634 | ||
8627 | static struct btrfs_block_group_cache * | 8635 | static struct btrfs_block_group_cache * |
@@ -8956,6 +8964,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, | |||
8956 | struct btrfs_root *tree_root = root->fs_info->tree_root; | 8964 | struct btrfs_root *tree_root = root->fs_info->tree_root; |
8957 | struct btrfs_key key; | 8965 | struct btrfs_key key; |
8958 | struct inode *inode; | 8966 | struct inode *inode; |
8967 | struct kobject *kobj = NULL; | ||
8959 | int ret; | 8968 | int ret; |
8960 | int index; | 8969 | int index; |
8961 | int factor; | 8970 | int factor; |
@@ -9055,11 +9064,15 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, | |||
9055 | */ | 9064 | */ |
9056 | list_del_init(&block_group->list); | 9065 | list_del_init(&block_group->list); |
9057 | if (list_empty(&block_group->space_info->block_groups[index])) { | 9066 | if (list_empty(&block_group->space_info->block_groups[index])) { |
9058 | kobject_del(&block_group->space_info->block_group_kobjs[index]); | 9067 | kobj = block_group->space_info->block_group_kobjs[index]; |
9059 | kobject_put(&block_group->space_info->block_group_kobjs[index]); | 9068 | block_group->space_info->block_group_kobjs[index] = NULL; |
9060 | clear_avail_alloc_bits(root->fs_info, block_group->flags); | 9069 | clear_avail_alloc_bits(root->fs_info, block_group->flags); |
9061 | } | 9070 | } |
9062 | up_write(&block_group->space_info->groups_sem); | 9071 | up_write(&block_group->space_info->groups_sem); |
9072 | if (kobj) { | ||
9073 | kobject_del(kobj); | ||
9074 | kobject_put(kobj); | ||
9075 | } | ||
9063 | 9076 | ||
9064 | if (block_group->cached == BTRFS_CACHE_STARTED) | 9077 | if (block_group->cached == BTRFS_CACHE_STARTED) |
9065 | wait_block_group_cache_done(block_group); | 9078 | wait_block_group_cache_done(block_group); |
diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c index c0dfda5644f2..df39458f1487 100644 --- a/fs/btrfs/sysfs.c +++ b/fs/btrfs/sysfs.c | |||
@@ -254,6 +254,7 @@ static ssize_t global_rsv_reserved_show(struct kobject *kobj, | |||
254 | BTRFS_ATTR(global_rsv_reserved, 0444, global_rsv_reserved_show); | 254 | BTRFS_ATTR(global_rsv_reserved, 0444, global_rsv_reserved_show); |
255 | 255 | ||
256 | #define to_space_info(_kobj) container_of(_kobj, struct btrfs_space_info, kobj) | 256 | #define to_space_info(_kobj) container_of(_kobj, struct btrfs_space_info, kobj) |
257 | #define to_raid_kobj(_kobj) container_of(_kobj, struct raid_kobject, kobj) | ||
257 | 258 | ||
258 | static ssize_t raid_bytes_show(struct kobject *kobj, | 259 | static ssize_t raid_bytes_show(struct kobject *kobj, |
259 | struct kobj_attribute *attr, char *buf); | 260 | struct kobj_attribute *attr, char *buf); |
@@ -266,7 +267,7 @@ static ssize_t raid_bytes_show(struct kobject *kobj, | |||
266 | { | 267 | { |
267 | struct btrfs_space_info *sinfo = to_space_info(kobj->parent); | 268 | struct btrfs_space_info *sinfo = to_space_info(kobj->parent); |
268 | struct btrfs_block_group_cache *block_group; | 269 | struct btrfs_block_group_cache *block_group; |
269 | int index = kobj - sinfo->block_group_kobjs; | 270 | int index = to_raid_kobj(kobj)->raid_type; |
270 | u64 val = 0; | 271 | u64 val = 0; |
271 | 272 | ||
272 | down_read(&sinfo->groups_sem); | 273 | down_read(&sinfo->groups_sem); |
@@ -288,7 +289,7 @@ static struct attribute *raid_attributes[] = { | |||
288 | 289 | ||
289 | static void release_raid_kobj(struct kobject *kobj) | 290 | static void release_raid_kobj(struct kobject *kobj) |
290 | { | 291 | { |
291 | kobject_put(kobj->parent); | 292 | kfree(to_raid_kobj(kobj)); |
292 | } | 293 | } |
293 | 294 | ||
294 | struct kobj_type btrfs_raid_ktype = { | 295 | struct kobj_type btrfs_raid_ktype = { |