Commit 96091341 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-5.16-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull btrfs fixes from David Sterba:
 "A few more fixes, almost all error handling one-liners and for stable.

   - regression fix in directory logging items

   - regression fix of extent buffer status bits handling after an error

   - fix memory leak in error handling path in tree-log

   - fix freeing invalid anon device number when handling errors during
     subvolume creation

   - fix warning when freeing leaf after subvolume creation failure

   - fix missing blkdev put in device scan error handling

   - fix invalid delayed ref after subvolume creation failure"

* tag 'for-5.16-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix missing blkdev_put() call in btrfs_scan_one_device()
  btrfs: fix warning when freeing leaf after subvolume creation failure
  btrfs: fix invalid delayed ref after subvolume creation failure
  btrfs: check WRITE_ERR when trying to read an extent buffer
  btrfs: fix missing last dir item offset update when logging directory
  btrfs: fix double free of anon_dev after failure to create subvolume
  btrfs: fix memory leak in __add_inode_ref()
parents f1f05ef3 4989d4a0
...@@ -463,8 +463,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, ...@@ -463,8 +463,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
BUG_ON(ret < 0); BUG_ON(ret < 0);
rcu_assign_pointer(root->node, cow); rcu_assign_pointer(root->node, cow);
btrfs_free_tree_block(trans, root, buf, parent_start, btrfs_free_tree_block(trans, btrfs_root_id(root), buf,
last_ref); parent_start, last_ref);
free_extent_buffer(buf); free_extent_buffer(buf);
add_root_to_dirty_list(root); add_root_to_dirty_list(root);
} else { } else {
...@@ -485,8 +485,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, ...@@ -485,8 +485,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
} }
btrfs_free_tree_block(trans, root, buf, parent_start, btrfs_free_tree_block(trans, btrfs_root_id(root), buf,
last_ref); parent_start, last_ref);
} }
if (unlock_orig) if (unlock_orig)
btrfs_tree_unlock(buf); btrfs_tree_unlock(buf);
...@@ -927,7 +927,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -927,7 +927,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
free_extent_buffer(mid); free_extent_buffer(mid);
root_sub_used(root, mid->len); root_sub_used(root, mid->len);
btrfs_free_tree_block(trans, root, mid, 0, 1); btrfs_free_tree_block(trans, btrfs_root_id(root), mid, 0, 1);
/* once for the root ptr */ /* once for the root ptr */
free_extent_buffer_stale(mid); free_extent_buffer_stale(mid);
return 0; return 0;
...@@ -986,7 +986,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -986,7 +986,8 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
btrfs_tree_unlock(right); btrfs_tree_unlock(right);
del_ptr(root, path, level + 1, pslot + 1); del_ptr(root, path, level + 1, pslot + 1);
root_sub_used(root, right->len); root_sub_used(root, right->len);
btrfs_free_tree_block(trans, root, right, 0, 1); btrfs_free_tree_block(trans, btrfs_root_id(root), right,
0, 1);
free_extent_buffer_stale(right); free_extent_buffer_stale(right);
right = NULL; right = NULL;
} else { } else {
...@@ -1031,7 +1032,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans, ...@@ -1031,7 +1032,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
btrfs_tree_unlock(mid); btrfs_tree_unlock(mid);
del_ptr(root, path, level + 1, pslot); del_ptr(root, path, level + 1, pslot);
root_sub_used(root, mid->len); root_sub_used(root, mid->len);
btrfs_free_tree_block(trans, root, mid, 0, 1); btrfs_free_tree_block(trans, btrfs_root_id(root), mid, 0, 1);
free_extent_buffer_stale(mid); free_extent_buffer_stale(mid);
mid = NULL; mid = NULL;
} else { } else {
...@@ -4032,7 +4033,7 @@ static noinline void btrfs_del_leaf(struct btrfs_trans_handle *trans, ...@@ -4032,7 +4033,7 @@ static noinline void btrfs_del_leaf(struct btrfs_trans_handle *trans,
root_sub_used(root, leaf->len); root_sub_used(root, leaf->len);
atomic_inc(&leaf->refs); atomic_inc(&leaf->refs);
btrfs_free_tree_block(trans, root, leaf, 0, 1); btrfs_free_tree_block(trans, btrfs_root_id(root), leaf, 0, 1);
free_extent_buffer_stale(leaf); free_extent_buffer_stale(leaf);
} }
/* /*
......
...@@ -2257,6 +2257,11 @@ static inline bool btrfs_root_dead(const struct btrfs_root *root) ...@@ -2257,6 +2257,11 @@ static inline bool btrfs_root_dead(const struct btrfs_root *root)
return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_DEAD)) != 0; return (root->root_item.flags & cpu_to_le64(BTRFS_ROOT_SUBVOL_DEAD)) != 0;
} }
static inline u64 btrfs_root_id(const struct btrfs_root *root)
{
return root->root_key.objectid;
}
/* struct btrfs_root_backup */ /* struct btrfs_root_backup */
BTRFS_SETGET_STACK_FUNCS(backup_tree_root, struct btrfs_root_backup, BTRFS_SETGET_STACK_FUNCS(backup_tree_root, struct btrfs_root_backup,
tree_root, 64); tree_root, 64);
...@@ -2719,7 +2724,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans, ...@@ -2719,7 +2724,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
u64 empty_size, u64 empty_size,
enum btrfs_lock_nesting nest); enum btrfs_lock_nesting nest);
void btrfs_free_tree_block(struct btrfs_trans_handle *trans, void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 root_id,
struct extent_buffer *buf, struct extent_buffer *buf,
u64 parent, int last_ref); u64 parent, int last_ref);
int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans, int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans,
......
...@@ -1732,6 +1732,14 @@ static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info, ...@@ -1732,6 +1732,14 @@ static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info,
} }
return root; return root;
fail: fail:
/*
* If our caller provided us an anonymous device, then it's his
* responsability to free it in case we fail. So we have to set our
* root's anon_dev to 0 to avoid a double free, once by btrfs_put_root()
* and once again by our caller.
*/
if (anon_dev)
root->anon_dev = 0;
btrfs_put_root(root); btrfs_put_root(root);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
......
...@@ -3275,20 +3275,20 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, ...@@ -3275,20 +3275,20 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
} }
void btrfs_free_tree_block(struct btrfs_trans_handle *trans, void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 root_id,
struct extent_buffer *buf, struct extent_buffer *buf,
u64 parent, int last_ref) u64 parent, int last_ref)
{ {
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_ref generic_ref = { 0 }; struct btrfs_ref generic_ref = { 0 };
int ret; int ret;
btrfs_init_generic_ref(&generic_ref, BTRFS_DROP_DELAYED_REF, btrfs_init_generic_ref(&generic_ref, BTRFS_DROP_DELAYED_REF,
buf->start, buf->len, parent); buf->start, buf->len, parent);
btrfs_init_tree_ref(&generic_ref, btrfs_header_level(buf), btrfs_init_tree_ref(&generic_ref, btrfs_header_level(buf),
root->root_key.objectid, 0, false); root_id, 0, false);
if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) { if (root_id != BTRFS_TREE_LOG_OBJECTID) {
btrfs_ref_tree_mod(fs_info, &generic_ref); btrfs_ref_tree_mod(fs_info, &generic_ref);
ret = btrfs_add_delayed_tree_ref(trans, &generic_ref, NULL); ret = btrfs_add_delayed_tree_ref(trans, &generic_ref, NULL);
BUG_ON(ret); /* -ENOMEM */ BUG_ON(ret); /* -ENOMEM */
...@@ -3298,7 +3298,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, ...@@ -3298,7 +3298,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_block_group *cache; struct btrfs_block_group *cache;
bool must_pin = false; bool must_pin = false;
if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) { if (root_id != BTRFS_TREE_LOG_OBJECTID) {
ret = check_ref_cleanup(trans, buf->start); ret = check_ref_cleanup(trans, buf->start);
if (!ret) { if (!ret) {
btrfs_redirty_list_add(trans->transaction, buf); btrfs_redirty_list_add(trans->transaction, buf);
...@@ -5472,7 +5472,8 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans, ...@@ -5472,7 +5472,8 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
goto owner_mismatch; goto owner_mismatch;
} }
btrfs_free_tree_block(trans, root, eb, parent, wc->refs[level] == 1); btrfs_free_tree_block(trans, btrfs_root_id(root), eb, parent,
wc->refs[level] == 1);
out: out:
wc->refs[level] = 0; wc->refs[level] = 0;
wc->flags[level] = 0; wc->flags[level] = 0;
......
...@@ -6611,6 +6611,14 @@ int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num) ...@@ -6611,6 +6611,14 @@ int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num)
if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags)) if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
return 0; return 0;
/*
* We could have had EXTENT_BUFFER_UPTODATE cleared by the write
* operation, which could potentially still be in flight. In this case
* we simply want to return an error.
*/
if (unlikely(test_bit(EXTENT_BUFFER_WRITE_ERR, &eb->bflags)))
return -EIO;
if (eb->fs_info->sectorsize < PAGE_SIZE) if (eb->fs_info->sectorsize < PAGE_SIZE)
return read_extent_buffer_subpage(eb, wait, mirror_num); return read_extent_buffer_subpage(eb, wait, mirror_num);
......
...@@ -1256,8 +1256,8 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info) ...@@ -1256,8 +1256,8 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
btrfs_tree_lock(free_space_root->node); btrfs_tree_lock(free_space_root->node);
btrfs_clean_tree_block(free_space_root->node); btrfs_clean_tree_block(free_space_root->node);
btrfs_tree_unlock(free_space_root->node); btrfs_tree_unlock(free_space_root->node);
btrfs_free_tree_block(trans, free_space_root, free_space_root->node, btrfs_free_tree_block(trans, btrfs_root_id(free_space_root),
0, 1); free_space_root->node, 0, 1);
btrfs_put_root(free_space_root); btrfs_put_root(free_space_root);
......
...@@ -617,11 +617,13 @@ static noinline int create_subvol(struct user_namespace *mnt_userns, ...@@ -617,11 +617,13 @@ static noinline int create_subvol(struct user_namespace *mnt_userns,
* Since we don't abort the transaction in this case, free the * Since we don't abort the transaction in this case, free the
* tree block so that we don't leak space and leave the * tree block so that we don't leak space and leave the
* filesystem in an inconsistent state (an extent item in the * filesystem in an inconsistent state (an extent item in the
* extent tree without backreferences). Also no need to have * extent tree with a backreference for a root that does not
* the tree block locked since it is not in any tree at this * exists).
* point, so no other task can find it and use it.
*/ */
btrfs_free_tree_block(trans, root, leaf, 0, 1); btrfs_tree_lock(leaf);
btrfs_clean_tree_block(leaf);
btrfs_tree_unlock(leaf);
btrfs_free_tree_block(trans, objectid, leaf, 0, 1);
free_extent_buffer(leaf); free_extent_buffer(leaf);
goto fail; goto fail;
} }
......
...@@ -1219,7 +1219,8 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info) ...@@ -1219,7 +1219,8 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
btrfs_tree_lock(quota_root->node); btrfs_tree_lock(quota_root->node);
btrfs_clean_tree_block(quota_root->node); btrfs_clean_tree_block(quota_root->node);
btrfs_tree_unlock(quota_root->node); btrfs_tree_unlock(quota_root->node);
btrfs_free_tree_block(trans, quota_root, quota_root->node, 0, 1); btrfs_free_tree_block(trans, btrfs_root_id(quota_root),
quota_root->node, 0, 1);
btrfs_put_root(quota_root); btrfs_put_root(quota_root);
......
...@@ -1181,6 +1181,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans, ...@@ -1181,6 +1181,7 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
parent_objectid, victim_name, parent_objectid, victim_name,
victim_name_len); victim_name_len);
if (ret < 0) { if (ret < 0) {
kfree(victim_name);
return ret; return ret;
} else if (!ret) { } else if (!ret) {
ret = -ENOENT; ret = -ENOENT;
...@@ -3977,6 +3978,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans, ...@@ -3977,6 +3978,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
goto done; goto done;
} }
if (btrfs_header_generation(path->nodes[0]) != trans->transid) { if (btrfs_header_generation(path->nodes[0]) != trans->transid) {
ctx->last_dir_item_offset = min_key.offset;
ret = overwrite_item(trans, log, dst_path, ret = overwrite_item(trans, log, dst_path,
path->nodes[0], path->slots[0], path->nodes[0], path->slots[0],
&min_key); &min_key);
......
...@@ -1370,8 +1370,10 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags, ...@@ -1370,8 +1370,10 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags,
bytenr_orig = btrfs_sb_offset(0); bytenr_orig = btrfs_sb_offset(0);
ret = btrfs_sb_log_location_bdev(bdev, 0, READ, &bytenr); ret = btrfs_sb_log_location_bdev(bdev, 0, READ, &bytenr);
if (ret) if (ret) {
return ERR_PTR(ret); device = ERR_PTR(ret);
goto error_bdev_put;
}
disk_super = btrfs_read_disk_super(bdev, bytenr, bytenr_orig); disk_super = btrfs_read_disk_super(bdev, bytenr, bytenr_orig);
if (IS_ERR(disk_super)) { if (IS_ERR(disk_super)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment