linux-btrfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/5] Serious fixes for different error paths
@ 2021-01-14 19:02 Josef Bacik
  2021-01-14 19:02 ` [PATCH v2 1/5] btrfs: fix reloc root leak with 0 ref reloc roots on recovery Josef Bacik
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Josef Bacik @ 2021-01-14 19:02 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

v1->v2:
- Rebased onto misc-next, dropping everything that's been merged so far.
- Fixed "btrfs: splice remaining dirty_bg's onto the transaction dirty bg list"
  to handle the btrfs_alloc_path() failure and cleaned up the error handling as
  a result of that change.
- dropped "btrfs: don't clear ret in btrfs_start_dirty_block_groups" as I fixed
  it differently in "btrfs: splice remaining dirty_bg's onto the transaction
  dirty bg list"
- Added a link to Zygo's original report in "btrfs: add asserts for deleting
  backref cache nodes".
- Clarified the error condition that lead to the WARN_ON() in the changelog for
  "btrfs: do not WARN_ON() if we can't find the reloc root".
- Added the stack trace that the error injection triggered in order to get the
  error that happened in "btrfs: abort the transaction if we fail to inc ref in
  btrfs_copy_root".

--- Original email ---
Hello,

A lot of these were in previous versions of the relocation error handling
patches.  I added a few since the last go around.  All of these do not rely on
the error handling patches, and some of them are quite important otherwise we
get corruption if we get errors in certain spots.  There's also a few lockdep
fixes and such.  These really need to go in ASAP, regardless of when the
relocation error handling patches are merged.  They're mostly small and self
contained, the only "big" one being the one that tracks the root owner for
relocation reads, which is to resolve the remaining class of lockdep errors we
get because of an improper lockdep class set on the extent buffer.  Thanks,

Josef

Josef Bacik (5):
  btrfs: fix reloc root leak with 0 ref reloc roots on recovery
  btrfs: splice remaining dirty_bg's onto the transaction dirty bg list
  btrfs: do not WARN_ON() if we can't find the reloc root
  btrfs: add asserts for deleting backref cache nodes
  btrfs: abort the transaction if we fail to inc ref in btrfs_copy_root

 fs/btrfs/backref.c     |  2 +-
 fs/btrfs/backref.h     |  9 ++++++---
 fs/btrfs/block-group.c | 19 ++++++++++++-------
 fs/btrfs/ctree.c       |  5 +++--
 fs/btrfs/relocation.c  |  4 +---
 5 files changed, 23 insertions(+), 16 deletions(-)

-- 
2.26.2


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/5] btrfs: fix reloc root leak with 0 ref reloc roots on recovery
  2021-01-14 19:02 [PATCH v2 0/5] Serious fixes for different error paths Josef Bacik
@ 2021-01-14 19:02 ` Josef Bacik
  2021-01-14 19:02 ` [PATCH v2 2/5] btrfs: splice remaining dirty_bg's onto the transaction dirty bg list Josef Bacik
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Josef Bacik @ 2021-01-14 19:02 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

When recovering a relocation, if we run into a reloc root that has 0
refs we simply add it to the reloc_control->reloc_roots list, and then
clean it up later.  The problem with this is __del_reloc_root() doesn't
do anything if the root isn't in the radix tree, which in this case it
won't be because we never call __add_reloc_root() on the reloc_root.

This exit condition simply isn't correct really.  During normal
operation we can remove ourselves from the rb tree and then we're meant
to clean up later at merge_reloc_roots() time, and this happens
correctly.  During recovery we're depending on free_reloc_roots() to
drop our references, but we're short-circuiting.

Fix this by continuing to check if we're on the list and dropping
ourselves from the reloc_control root list and dropping our reference
appropriately.  Change the corresponding BUG_ON() to an ASSERT() that
does the correct thing if we aren't in the rb tree.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/relocation.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 9f2289bcdde6..d29baf3822a7 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -669,9 +669,7 @@ static void __del_reloc_root(struct btrfs_root *root)
 			RB_CLEAR_NODE(&node->rb_node);
 		}
 		spin_unlock(&rc->reloc_root_tree.lock);
-		if (!node)
-			return;
-		BUG_ON((struct btrfs_root *)node->data != root);
+		ASSERT(!node || (struct btrfs_root *)node->data == root);
 	}
 
 	/*
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 2/5] btrfs: splice remaining dirty_bg's onto the transaction dirty bg list
  2021-01-14 19:02 [PATCH v2 0/5] Serious fixes for different error paths Josef Bacik
  2021-01-14 19:02 ` [PATCH v2 1/5] btrfs: fix reloc root leak with 0 ref reloc roots on recovery Josef Bacik
@ 2021-01-14 19:02 ` Josef Bacik
  2021-01-14 19:02 ` [PATCH v2 3/5] btrfs: do not WARN_ON() if we can't find the reloc root Josef Bacik
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Josef Bacik @ 2021-01-14 19:02 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

While doing error injection testing with my relocation patches I hit the
following ASSERT()

assertion failed: list_empty(&block_group->dirty_list), in fs/btrfs/block-group.c:3356
------------[ cut here ]------------
kernel BUG at fs/btrfs/ctree.h:3357!
invalid opcode: 0000 [#1] SMP NOPTI
CPU: 0 PID: 24351 Comm: umount Tainted: G        W         5.10.0-rc3+ #193
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-2.fc32 04/01/2014
RIP: 0010:assertfail.constprop.0+0x18/0x1a
RSP: 0018:ffffa09b019c7e00 EFLAGS: 00010282
RAX: 0000000000000056 RBX: ffff8f6492c18000 RCX: 0000000000000000
RDX: ffff8f64fbc27c60 RSI: ffff8f64fbc19050 RDI: ffff8f64fbc19050
RBP: ffff8f6483bbdc00 R08: 0000000000000000 R09: 0000000000000000
R10: ffffa09b019c7c38 R11: ffffffff85d70928 R12: ffff8f6492c18100
R13: ffff8f6492c18148 R14: ffff8f6483bbdd70 R15: dead000000000100
FS:  00007fbfda4cdc40(0000) GS:ffff8f64fbc00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fbfda666fd0 CR3: 000000013cf66002 CR4: 0000000000370ef0
Call Trace:
 btrfs_free_block_groups.cold+0x55/0x55
 close_ctree+0x2c5/0x306
 ? fsnotify_destroy_marks+0x14/0x100
 generic_shutdown_super+0x6c/0x100
 kill_anon_super+0x14/0x30
 btrfs_kill_super+0x12/0x20
 deactivate_locked_super+0x36/0xa0
 cleanup_mnt+0x12d/0x190
 task_work_run+0x5c/0xa0
 exit_to_user_mode_prepare+0x1b1/0x1d0
 syscall_exit_to_user_mode+0x54/0x280
 entry_SYSCALL_64_after_hwframe+0x44/0xa9

This happened because I injected an error in btrfs_cow_block() while
running the dirty block groups.  When we run the dirty block groups, we
splice the list onto a local list to process.  However if an error
occurs, we only cleanup the transactions dirty block group list, not any
pending block groups we have on our locally spliced list.

In fact if we fail to allocate a path in this function we'll also fail
to clean up the splice list.

Fix this by splicing the list back onto the transaction dirty block
group list so that the block groups are cleaned up.  Then add a 'out'
label and have the error conditions jump to out so that the errors are
handled properly.  This also has the side-effect of fixing a problem
where we would clear 'ret' on error because we unconditionally ran
btrfs_run_delayed_refs().

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/block-group.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c
index 0886e81e5540..73632782d0cd 100644
--- a/fs/btrfs/block-group.c
+++ b/fs/btrfs/block-group.c
@@ -2556,8 +2556,10 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans)
 
 	if (!path) {
 		path = btrfs_alloc_path();
-		if (!path)
-			return -ENOMEM;
+		if (!path) {
+			ret = -ENOMEM;
+			goto out;
+		}
 	}
 
 	/*
@@ -2651,16 +2653,14 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans)
 			btrfs_put_block_group(cache);
 		if (drop_reserve)
 			btrfs_delayed_refs_rsv_release(fs_info, 1);
-
-		if (ret)
-			break;
-
 		/*
 		 * Avoid blocking other tasks for too long. It might even save
 		 * us from writing caches for block groups that are going to be
 		 * removed.
 		 */
 		mutex_unlock(&trans->transaction->cache_write_mutex);
+		if (ret)
+			goto out;
 		mutex_lock(&trans->transaction->cache_write_mutex);
 	}
 	mutex_unlock(&trans->transaction->cache_write_mutex);
@@ -2684,7 +2684,12 @@ int btrfs_start_dirty_block_groups(struct btrfs_trans_handle *trans)
 			goto again;
 		}
 		spin_unlock(&cur_trans->dirty_bgs_lock);
-	} else if (ret < 0) {
+	}
+out:
+	if (ret < 0) {
+		spin_lock(&cur_trans->dirty_bgs_lock);
+		list_splice_init(&dirty, &cur_trans->dirty_bgs);
+		spin_unlock(&cur_trans->dirty_bgs_lock);
 		btrfs_cleanup_dirty_bgs(cur_trans, fs_info);
 	}
 
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 3/5] btrfs: do not WARN_ON() if we can't find the reloc root
  2021-01-14 19:02 [PATCH v2 0/5] Serious fixes for different error paths Josef Bacik
  2021-01-14 19:02 ` [PATCH v2 1/5] btrfs: fix reloc root leak with 0 ref reloc roots on recovery Josef Bacik
  2021-01-14 19:02 ` [PATCH v2 2/5] btrfs: splice remaining dirty_bg's onto the transaction dirty bg list Josef Bacik
@ 2021-01-14 19:02 ` Josef Bacik
  2021-01-14 19:02 ` [PATCH v2 4/5] btrfs: add asserts for deleting backref cache nodes Josef Bacik
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Josef Bacik @ 2021-01-14 19:02 UTC (permalink / raw)
  To: linux-btrfs, kernel-team; +Cc: Zygo Blaxell

The backref code is looking for a reloc_root that corresponds to the
given fs root.  However any number of things could have gone wrong while
initializing that reloc_root, like ENOMEM while trying to allocate the
root itself, or EIO while trying to write the root item.  This would
result in no corresponding reloc_root being in the reloc root cache, and
thus would return NULL when we do the find_reloc_root() call.

Because of this we do not want to WARN_ON().  This presumably was meant
to catch developer errors, cases where we messed up adding the reloc
root.  However we can easily hit this case with error injection, and
thus should not do a WARN_ON().

Reported-by: Zygo Blaxell <ce3g8jdj@umail.furryterror.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/backref.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index ef71aba5bc15..7ac59a568595 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -2617,7 +2617,7 @@ static int handle_direct_tree_backref(struct btrfs_backref_cache *cache,
 		/* Only reloc backref cache cares about a specific root */
 		if (cache->is_reloc) {
 			root = find_reloc_root(cache->fs_info, cur->bytenr);
-			if (WARN_ON(!root))
+			if (!root)
 				return -ENOENT;
 			cur->root = root;
 		} else {
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 4/5] btrfs: add asserts for deleting backref cache nodes
  2021-01-14 19:02 [PATCH v2 0/5] Serious fixes for different error paths Josef Bacik
                   ` (2 preceding siblings ...)
  2021-01-14 19:02 ` [PATCH v2 3/5] btrfs: do not WARN_ON() if we can't find the reloc root Josef Bacik
@ 2021-01-14 19:02 ` Josef Bacik
  2021-01-14 19:02 ` [PATCH v2 5/5] btrfs: abort the transaction if we fail to inc ref in btrfs_copy_root Josef Bacik
  2021-01-26 16:41 ` [PATCH v2 0/5] Serious fixes for different error paths David Sterba
  5 siblings, 0 replies; 7+ messages in thread
From: Josef Bacik @ 2021-01-14 19:02 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

A weird KASAN problem that Zygo reported could have been easily caught
if we checked for basic things in our backref freeing code.  We have two
methods of freeing a backref node

- btrfs_backref_free_node: this just is kfree() essentially.
- btrfs_backref_drop_node: this actually unlinks the node and cleans up
  everything and then calls btrfs_backref_free_node().

We should mostly be using btrfs_backref_drop_node(), to make sure the
node is properly unlinked from the backref cache, and only use
btrfs_backref_free_node() when we know the node isn't actually linked to
the backref cache.  We made a mistake here and thus got the KASAN splat.
Make this style of issue easier to find by adding some ASSERT()'s to
btrfs_backref_free_node() and adjusting our deletion stuff to properly
init the list so we can rely on list_empty() checks working properly.

Link: https://lore.kernel.org/linux-btrfs/20201208194607.GI31381@hungrycats.org/
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/backref.h | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h
index ff705cc564a9..17abde7f794c 100644
--- a/fs/btrfs/backref.h
+++ b/fs/btrfs/backref.h
@@ -296,6 +296,9 @@ static inline void btrfs_backref_free_node(struct btrfs_backref_cache *cache,
 					   struct btrfs_backref_node *node)
 {
 	if (node) {
+		ASSERT(list_empty(&node->list));
+		ASSERT(list_empty(&node->lower));
+		ASSERT(node->eb == NULL);
 		cache->nr_nodes--;
 		btrfs_put_root(node->root);
 		kfree(node);
@@ -340,11 +343,11 @@ static inline void btrfs_backref_drop_node_buffer(
 static inline void btrfs_backref_drop_node(struct btrfs_backref_cache *tree,
 					   struct btrfs_backref_node *node)
 {
-	BUG_ON(!list_empty(&node->upper));
+	ASSERT(list_empty(&node->upper));
 
 	btrfs_backref_drop_node_buffer(node);
-	list_del(&node->list);
-	list_del(&node->lower);
+	list_del_init(&node->list);
+	list_del_init(&node->lower);
 	if (!RB_EMPTY_NODE(&node->rb_node))
 		rb_erase(&node->rb_node, &tree->rb_root);
 	btrfs_backref_free_node(tree, node);
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 5/5] btrfs: abort the transaction if we fail to inc ref in btrfs_copy_root
  2021-01-14 19:02 [PATCH v2 0/5] Serious fixes for different error paths Josef Bacik
                   ` (3 preceding siblings ...)
  2021-01-14 19:02 ` [PATCH v2 4/5] btrfs: add asserts for deleting backref cache nodes Josef Bacik
@ 2021-01-14 19:02 ` Josef Bacik
  2021-01-26 16:41 ` [PATCH v2 0/5] Serious fixes for different error paths David Sterba
  5 siblings, 0 replies; 7+ messages in thread
From: Josef Bacik @ 2021-01-14 19:02 UTC (permalink / raw)
  To: linux-btrfs, kernel-team

While testing my error handling patches, I added a error injection site
at btrfs_inc_extent_ref, to validate the error handling I added was
doing the correct thing.  However I hit a pretty ugly corruption while
doing this check, with the following error injection stack trace

btrfs_inc_extent_ref
btrfs_copy_root
create_reloc_root
otrfs_init_reloc_root
btrfs_record_root_in_trans
btrfs_start_transaction
btrfs_update_inode
btrfs_update_time
touch_atime
file_accessed
btrfs_file_mmap

This is because we do not catch the error from btrfs_inc_extent_ref,
which in practice would be -ENOMEM, which means we lose the extent
references for a root that has already been allocated and inserted,
which is the problem.  Fix this by aborting the transaction if we fail
to do the reference modification.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/btrfs/ctree.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 56e132d825a2..95d9bae764ab 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -221,9 +221,10 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
 		ret = btrfs_inc_ref(trans, root, cow, 1);
 	else
 		ret = btrfs_inc_ref(trans, root, cow, 0);
-
-	if (ret)
+	if (ret) {
+		btrfs_abort_transaction(trans, ret);
 		return ret;
+	}
 
 	btrfs_mark_buffer_dirty(cow);
 	*cow_ret = cow;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 0/5] Serious fixes for different error paths
  2021-01-14 19:02 [PATCH v2 0/5] Serious fixes for different error paths Josef Bacik
                   ` (4 preceding siblings ...)
  2021-01-14 19:02 ` [PATCH v2 5/5] btrfs: abort the transaction if we fail to inc ref in btrfs_copy_root Josef Bacik
@ 2021-01-26 16:41 ` David Sterba
  5 siblings, 0 replies; 7+ messages in thread
From: David Sterba @ 2021-01-26 16:41 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-btrfs, kernel-team

On Thu, Jan 14, 2021 at 02:02:41PM -0500, Josef Bacik wrote:
> v1->v2:
> - Rebased onto misc-next, dropping everything that's been merged so far.
> - Fixed "btrfs: splice remaining dirty_bg's onto the transaction dirty bg list"
>   to handle the btrfs_alloc_path() failure and cleaned up the error handling as
>   a result of that change.
> - dropped "btrfs: don't clear ret in btrfs_start_dirty_block_groups" as I fixed
>   it differently in "btrfs: splice remaining dirty_bg's onto the transaction
>   dirty bg list"
> - Added a link to Zygo's original report in "btrfs: add asserts for deleting
>   backref cache nodes".
> - Clarified the error condition that lead to the WARN_ON() in the changelog for
>   "btrfs: do not WARN_ON() if we can't find the reloc root".
> - Added the stack trace that the error injection triggered in order to get the
>   error that happened in "btrfs: abort the transaction if we fail to inc ref in
>   btrfs_copy_root".

Added to misc-next, thanks.

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-01-26 23:06 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-14 19:02 [PATCH v2 0/5] Serious fixes for different error paths Josef Bacik
2021-01-14 19:02 ` [PATCH v2 1/5] btrfs: fix reloc root leak with 0 ref reloc roots on recovery Josef Bacik
2021-01-14 19:02 ` [PATCH v2 2/5] btrfs: splice remaining dirty_bg's onto the transaction dirty bg list Josef Bacik
2021-01-14 19:02 ` [PATCH v2 3/5] btrfs: do not WARN_ON() if we can't find the reloc root Josef Bacik
2021-01-14 19:02 ` [PATCH v2 4/5] btrfs: add asserts for deleting backref cache nodes Josef Bacik
2021-01-14 19:02 ` [PATCH v2 5/5] btrfs: abort the transaction if we fail to inc ref in btrfs_copy_root Josef Bacik
2021-01-26 16:41 ` [PATCH v2 0/5] Serious fixes for different error paths David Sterba

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).