Commit 92825b02 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull btrfs fixes from David Sterba:
 "Correctness and a deadlock fixes"

* tag 'for-5.1-part2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: zstd: ensure reclaim timer is properly cleaned up
  btrfs: move ulist allocation out of transaction in quota enable
  btrfs: save drop_progress if we drop refs at all
  btrfs: check for refs on snapshot delete resume
  Btrfs: fix deadlock between clone/dedupe and rename
  Btrfs: fix corruption reading shared and compressed extents after hole punching
parents 1fbf3e48 d3865159
...@@ -1210,6 +1210,8 @@ enum { ...@@ -1210,6 +1210,8 @@ enum {
* Set for the subvolume tree owning the reloc tree. * Set for the subvolume tree owning the reloc tree.
*/ */
BTRFS_ROOT_DEAD_RELOC_TREE, BTRFS_ROOT_DEAD_RELOC_TREE,
/* Mark dead root stored on device whose cleanup needs to be resumed */
BTRFS_ROOT_DEAD_TREE,
}; };
/* /*
......
...@@ -8764,6 +8764,8 @@ struct walk_control { ...@@ -8764,6 +8764,8 @@ struct walk_control {
u64 refs[BTRFS_MAX_LEVEL]; u64 refs[BTRFS_MAX_LEVEL];
u64 flags[BTRFS_MAX_LEVEL]; u64 flags[BTRFS_MAX_LEVEL];
struct btrfs_key update_progress; struct btrfs_key update_progress;
struct btrfs_key drop_progress;
int drop_level;
int stage; int stage;
int level; int level;
int shared_level; int shared_level;
...@@ -8771,6 +8773,7 @@ struct walk_control { ...@@ -8771,6 +8773,7 @@ struct walk_control {
int keep_locks; int keep_locks;
int reada_slot; int reada_slot;
int reada_count; int reada_count;
int restarted;
}; };
#define DROP_REFERENCE 1 #define DROP_REFERENCE 1
...@@ -8933,6 +8936,33 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, ...@@ -8933,6 +8936,33 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans,
return 0; return 0;
} }
/*
* This is used to verify a ref exists for this root to deal with a bug where we
* would have a drop_progress key that hadn't been updated properly.
*/
static int check_ref_exists(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, u64 parent,
int level)
{
struct btrfs_path *path;
struct btrfs_extent_inline_ref *iref;
int ret;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
ret = lookup_extent_backref(trans, path, &iref, bytenr,
root->fs_info->nodesize, parent,
root->root_key.objectid, level, 0);
btrfs_free_path(path);
if (ret == -ENOENT)
return 0;
if (ret < 0)
return ret;
return 1;
}
/* /*
* helper to process tree block pointer. * helper to process tree block pointer.
* *
...@@ -9087,6 +9117,23 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, ...@@ -9087,6 +9117,23 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
parent = 0; parent = 0;
} }
/*
* If we had a drop_progress we need to verify the refs are set
* as expected. If we find our ref then we know that from here
* on out everything should be correct, and we can clear the
* ->restarted flag.
*/
if (wc->restarted) {
ret = check_ref_exists(trans, root, bytenr, parent,
level - 1);
if (ret < 0)
goto out_unlock;
if (ret == 0)
goto no_delete;
ret = 0;
wc->restarted = 0;
}
/* /*
* Reloc tree doesn't contribute to qgroup numbers, and we have * Reloc tree doesn't contribute to qgroup numbers, and we have
* already accounted them at merge time (replace_path), * already accounted them at merge time (replace_path),
...@@ -9102,13 +9149,23 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, ...@@ -9102,13 +9149,23 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
ret); ret);
} }
} }
/*
* We need to update the next key in our walk control so we can
* update the drop_progress key accordingly. We don't care if
* find_next_key doesn't find a key because that means we're at
* the end and are going to clean up now.
*/
wc->drop_level = level;
find_next_key(path, level, &wc->drop_progress);
ret = btrfs_free_extent(trans, root, bytenr, fs_info->nodesize, ret = btrfs_free_extent(trans, root, bytenr, fs_info->nodesize,
parent, root->root_key.objectid, parent, root->root_key.objectid,
level - 1, 0); level - 1, 0);
if (ret) if (ret)
goto out_unlock; goto out_unlock;
} }
no_delete:
*lookup_info = 1; *lookup_info = 1;
ret = 1; ret = 1;
...@@ -9425,6 +9482,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root, ...@@ -9425,6 +9482,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
} }
} }
wc->restarted = test_bit(BTRFS_ROOT_DEAD_TREE, &root->state);
wc->level = level; wc->level = level;
wc->shared_level = -1; wc->shared_level = -1;
wc->stage = DROP_REFERENCE; wc->stage = DROP_REFERENCE;
...@@ -9452,12 +9510,14 @@ int btrfs_drop_snapshot(struct btrfs_root *root, ...@@ -9452,12 +9510,14 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
} }
if (wc->stage == DROP_REFERENCE) { if (wc->stage == DROP_REFERENCE) {
level = wc->level; wc->drop_level = wc->level;
btrfs_node_key(path->nodes[level], btrfs_node_key_to_cpu(path->nodes[wc->drop_level],
&root_item->drop_progress, &wc->drop_progress,
path->slots[level]); path->slots[wc->drop_level]);
root_item->drop_level = level; }
} btrfs_cpu_key_to_disk(&root_item->drop_progress,
&wc->drop_progress);
root_item->drop_level = wc->drop_level;
BUG_ON(wc->level == 0); BUG_ON(wc->level == 0);
if (btrfs_should_end_transaction(trans) || if (btrfs_should_end_transaction(trans) ||
......
...@@ -2995,11 +2995,11 @@ static int __do_readpage(struct extent_io_tree *tree, ...@@ -2995,11 +2995,11 @@ static int __do_readpage(struct extent_io_tree *tree,
*/ */
if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags) && if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags) &&
prev_em_start && *prev_em_start != (u64)-1 && prev_em_start && *prev_em_start != (u64)-1 &&
*prev_em_start != em->orig_start) *prev_em_start != em->start)
force_bio_submit = true; force_bio_submit = true;
if (prev_em_start) if (prev_em_start)
*prev_em_start = em->orig_start; *prev_em_start = em->start;
free_extent_map(em); free_extent_map(em);
em = NULL; em = NULL;
......
...@@ -3207,21 +3207,6 @@ static long btrfs_ioctl_dev_info(struct btrfs_fs_info *fs_info, ...@@ -3207,21 +3207,6 @@ static long btrfs_ioctl_dev_info(struct btrfs_fs_info *fs_info,
return ret; return ret;
} }
static void btrfs_double_inode_unlock(struct inode *inode1, struct inode *inode2)
{
inode_unlock(inode1);
inode_unlock(inode2);
}
static void btrfs_double_inode_lock(struct inode *inode1, struct inode *inode2)
{
if (inode1 < inode2)
swap(inode1, inode2);
inode_lock_nested(inode1, I_MUTEX_PARENT);
inode_lock_nested(inode2, I_MUTEX_CHILD);
}
static void btrfs_double_extent_unlock(struct inode *inode1, u64 loff1, static void btrfs_double_extent_unlock(struct inode *inode1, u64 loff1,
struct inode *inode2, u64 loff2, u64 len) struct inode *inode2, u64 loff2, u64 len)
{ {
...@@ -3956,7 +3941,7 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in, ...@@ -3956,7 +3941,7 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in,
if (same_inode) if (same_inode)
inode_lock(inode_in); inode_lock(inode_in);
else else
btrfs_double_inode_lock(inode_in, inode_out); lock_two_nondirectories(inode_in, inode_out);
/* don't make the dst file partly checksummed */ /* don't make the dst file partly checksummed */
if ((BTRFS_I(inode_in)->flags & BTRFS_INODE_NODATASUM) != if ((BTRFS_I(inode_in)->flags & BTRFS_INODE_NODATASUM) !=
...@@ -4013,7 +3998,7 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in, ...@@ -4013,7 +3998,7 @@ static int btrfs_remap_file_range_prep(struct file *file_in, loff_t pos_in,
if (same_inode) if (same_inode)
inode_unlock(inode_in); inode_unlock(inode_in);
else else
btrfs_double_inode_unlock(inode_in, inode_out); unlock_two_nondirectories(inode_in, inode_out);
return ret; return ret;
} }
...@@ -4043,7 +4028,7 @@ loff_t btrfs_remap_file_range(struct file *src_file, loff_t off, ...@@ -4043,7 +4028,7 @@ loff_t btrfs_remap_file_range(struct file *src_file, loff_t off,
if (same_inode) if (same_inode)
inode_unlock(src_inode); inode_unlock(src_inode);
else else
btrfs_double_inode_unlock(src_inode, dst_inode); unlock_two_nondirectories(src_inode, dst_inode);
return ret < 0 ? ret : len; return ret < 0 ? ret : len;
} }
......
...@@ -894,6 +894,12 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info) ...@@ -894,6 +894,12 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
if (fs_info->quota_root) if (fs_info->quota_root)
goto out; goto out;
fs_info->qgroup_ulist = ulist_alloc(GFP_KERNEL);
if (!fs_info->qgroup_ulist) {
ret = -ENOMEM;
goto out;
}
/* /*
* 1 for quota root item * 1 for quota root item
* 1 for BTRFS_QGROUP_STATUS item * 1 for BTRFS_QGROUP_STATUS item
...@@ -909,13 +915,6 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info) ...@@ -909,13 +915,6 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
goto out; goto out;
} }
fs_info->qgroup_ulist = ulist_alloc(GFP_KERNEL);
if (!fs_info->qgroup_ulist) {
ret = -ENOMEM;
btrfs_abort_transaction(trans, ret);
goto out;
}
/* /*
* initially create the quota tree * initially create the quota tree
*/ */
......
...@@ -263,8 +263,10 @@ int btrfs_find_orphan_roots(struct btrfs_fs_info *fs_info) ...@@ -263,8 +263,10 @@ int btrfs_find_orphan_roots(struct btrfs_fs_info *fs_info)
if (root) { if (root) {
WARN_ON(!test_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED, WARN_ON(!test_bit(BTRFS_ROOT_ORPHAN_ITEM_INSERTED,
&root->state)); &root->state));
if (btrfs_root_refs(&root->root_item) == 0) if (btrfs_root_refs(&root->root_item) == 0) {
set_bit(BTRFS_ROOT_DEAD_TREE, &root->state);
btrfs_add_dead_root(root); btrfs_add_dead_root(root);
}
continue; continue;
} }
...@@ -310,8 +312,10 @@ int btrfs_find_orphan_roots(struct btrfs_fs_info *fs_info) ...@@ -310,8 +312,10 @@ int btrfs_find_orphan_roots(struct btrfs_fs_info *fs_info)
break; break;
} }
if (btrfs_root_refs(&root->root_item) == 0) if (btrfs_root_refs(&root->root_item) == 0) {
set_bit(BTRFS_ROOT_DEAD_TREE, &root->state);
btrfs_add_dead_root(root); btrfs_add_dead_root(root);
}
} }
btrfs_free_path(path); btrfs_free_path(path);
......
...@@ -195,8 +195,7 @@ static void zstd_cleanup_workspace_manager(void) ...@@ -195,8 +195,7 @@ static void zstd_cleanup_workspace_manager(void)
struct workspace *workspace; struct workspace *workspace;
int i; int i;
del_timer(&wsm.timer); spin_lock(&wsm.lock);
for (i = 0; i < ZSTD_BTRFS_MAX_LEVEL; i++) { for (i = 0; i < ZSTD_BTRFS_MAX_LEVEL; i++) {
while (!list_empty(&wsm.idle_ws[i])) { while (!list_empty(&wsm.idle_ws[i])) {
workspace = container_of(wsm.idle_ws[i].next, workspace = container_of(wsm.idle_ws[i].next,
...@@ -206,6 +205,9 @@ static void zstd_cleanup_workspace_manager(void) ...@@ -206,6 +205,9 @@ static void zstd_cleanup_workspace_manager(void)
wsm.ops->free_workspace(&workspace->list); wsm.ops->free_workspace(&workspace->list);
} }
} }
spin_unlock(&wsm.lock);
del_timer_sync(&wsm.timer);
} }
/* /*
......
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