Jiri Slaby 1f0641
From: Zhihao Cheng <chengzhihao1@huawei.com>
Jiri Slaby 1f0641
Date: Fri, 18 Nov 2022 17:02:35 +0800
Jiri Slaby 1f0641
Subject: [PATCH] ubifs: Re-statistic cleaned znode count if commit failed
Jiri Slaby 1f0641
References: bsc#1012628
Jiri Slaby 1f0641
Patch-mainline: 6.2.5
Jiri Slaby 1f0641
Git-commit: 944e096aa24071d3fe22822f6249d3ae309e39ea
Jiri Slaby 1f0641
Jiri Slaby 1f0641
[ Upstream commit 944e096aa24071d3fe22822f6249d3ae309e39ea ]
Jiri Slaby 1f0641
Jiri Slaby 1f0641
Dirty znodes will be written on flash in committing process with
Jiri Slaby 1f0641
following states:
Jiri Slaby 1f0641
Jiri Slaby 1f0641
	      process A			|  znode state
Jiri Slaby 1f0641
------------------------------------------------------
Jiri Slaby 1f0641
do_commit				| DIRTY_ZNODE
Jiri Slaby 1f0641
  ubifs_tnc_start_commit		| DIRTY_ZNODE
Jiri Slaby 1f0641
   get_znodes_to_commit			| DIRTY_ZNODE | COW_ZNODE
Jiri Slaby 1f0641
    layout_commit			| DIRTY_ZNODE | COW_ZNODE
Jiri Slaby 1f0641
     fill_gap                           | 0
Jiri Slaby 1f0641
  write master				| 0 or OBSOLETE_ZNODE
Jiri Slaby 1f0641
Jiri Slaby 1f0641
	      process B			|  znode state
Jiri Slaby 1f0641
------------------------------------------------------
Jiri Slaby 1f0641
do_commit				| DIRTY_ZNODE[1]
Jiri Slaby 1f0641
  ubifs_tnc_start_commit		| DIRTY_ZNODE
Jiri Slaby 1f0641
   get_znodes_to_commit			| DIRTY_ZNODE | COW_ZNODE
Jiri Slaby 1f0641
  ubifs_tnc_end_commit			| DIRTY_ZNODE | COW_ZNODE
Jiri Slaby 1f0641
   write_index                          | 0
Jiri Slaby 1f0641
  write master				| 0 or OBSOLETE_ZNODE[2] or
Jiri Slaby 1f0641
					| DIRTY_ZNODE[3]
Jiri Slaby 1f0641
Jiri Slaby 1f0641
[1] znode is dirtied without concurrent committing process
Jiri Slaby 1f0641
[2] znode is copied up (re-dirtied by other process) before cleaned
Jiri Slaby 1f0641
    up in committing process
Jiri Slaby 1f0641
[3] znode is re-dirtied after cleaned up in committing process
Jiri Slaby 1f0641
Jiri Slaby 1f0641
Currently, the clean znode count is updated in free_obsolete_znodes(),
Jiri Slaby 1f0641
which is called only in normal path. If do_commit failed, clean znode
Jiri Slaby 1f0641
count won't be updated, which triggers a failure ubifs assertion[4] in
Jiri Slaby 1f0641
ubifs_tnc_close():
Jiri Slaby 1f0641
 ubifs_assert_failed [ubifs]: UBIFS assert failed: freed == n
Jiri Slaby 1f0641
Jiri Slaby 1f0641
[4] Commit 380347e9ca7682 ("UBIFS: Add an assertion for clean_zn_cnt").
Jiri Slaby 1f0641
Jiri Slaby 1f0641
Fix it by re-statisticing cleaned znode count in tnc_destroy_cnext().
Jiri Slaby 1f0641
Jiri Slaby 1f0641
Fetch a reproducer in [Link].
Jiri Slaby 1f0641
Jiri Slaby 1f0641
Link: https://bugzilla.kernel.org/show_bug.cgi?id=216704
Jiri Slaby 1f0641
Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Jiri Slaby 1f0641
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Jiri Slaby 1f0641
Signed-off-by: Richard Weinberger <richard@nod.at>
Jiri Slaby 1f0641
Signed-off-by: Sasha Levin <sashal@kernel.org>
Jiri Slaby 1f0641
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Jiri Slaby 1f0641
---
Jiri Slaby 1f0641
 fs/ubifs/tnc.c | 15 +++++++++++++++
Jiri Slaby 1f0641
 1 file changed, 15 insertions(+)
Jiri Slaby 1f0641
Jiri Slaby 1f0641
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
Jiri Slaby 1f0641
index 488f3da7..2df56bbc 100644
Jiri Slaby 1f0641
--- a/fs/ubifs/tnc.c
Jiri Slaby 1f0641
+++ b/fs/ubifs/tnc.c
Jiri Slaby 1f0641
@@ -3053,6 +3053,21 @@ static void tnc_destroy_cnext(struct ubifs_info *c)
Jiri Slaby 1f0641
 		cnext = cnext->cnext;
Jiri Slaby 1f0641
 		if (ubifs_zn_obsolete(znode))
Jiri Slaby 1f0641
 			kfree(znode);
Jiri Slaby 1f0641
+		else if (!ubifs_zn_cow(znode)) {
Jiri Slaby 1f0641
+			/*
Jiri Slaby 1f0641
+			 * Don't forget to update clean znode count after
Jiri Slaby 1f0641
+			 * committing failed, because ubifs will check this
Jiri Slaby 1f0641
+			 * count while closing tnc. Non-obsolete znode could
Jiri Slaby 1f0641
+			 * be re-dirtied during committing process, so dirty
Jiri Slaby 1f0641
+			 * flag is untrustable. The flag 'COW_ZNODE' is set
Jiri Slaby 1f0641
+			 * for each dirty znode before committing, and it is
Jiri Slaby 1f0641
+			 * cleared as long as the znode become clean, so we
Jiri Slaby 1f0641
+			 * can statistic clean znode count according to this
Jiri Slaby 1f0641
+			 * flag.
Jiri Slaby 1f0641
+			 */
Jiri Slaby 1f0641
+			atomic_long_inc(&c->clean_zn_cnt);
Jiri Slaby 1f0641
+			atomic_long_inc(&ubifs_clean_zn_cnt);
Jiri Slaby 1f0641
+		}
Jiri Slaby 1f0641
 	} while (cnext && cnext != c->cnext);
Jiri Slaby 1f0641
 }
Jiri Slaby 1f0641
 
Jiri Slaby 1f0641
-- 
Jiri Slaby 1f0641
2.35.3
Jiri Slaby 1f0641