| From 028df971eec7f04e89b583a6a29c900ff0572a9e Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 20 Jun 2024 15:24:05 +0800 |
| Subject: jbd2: avoid mount failed when commit block is partial submitted |
| |
| From: Ye Bin <yebin10@huawei.com> |
| |
| [ Upstream commit 0bab8db4152c4a2185a1367db09cc402bdc62d5e ] |
| |
| We encountered a problem that the file system could not be mounted in |
| the power-off scenario. The analysis of the file system mirror shows that |
| only part of the data is written to the last commit block. |
| The valid data of the commit block is concentrated in the first sector. |
| However, the data of the entire block is involved in the checksum calculation. |
| For different hardware, the minimum atomic unit may be different. |
| If the checksum of a committed block is incorrect, clear the data except the |
| 'commit_header' and then calculate the checksum. If the checkusm is correct, |
| it is considered that the block is partially committed, Then continue to replay |
| journal. |
| |
| Signed-off-by: Ye Bin <yebin10@huawei.com> |
| Reviewed-by: Jan Kara <jack@suse.cz> |
| Link: https://patch.msgid.link/20240620072405.3533701-1-yebin@huaweicloud.com |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/jbd2/recovery.c | 30 ++++++++++++++++++++++++++++++ |
| 1 file changed, 30 insertions(+) |
| |
| diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c |
| index 1f7664984d6e..0d14b5f39be6 100644 |
| --- a/fs/jbd2/recovery.c |
| +++ b/fs/jbd2/recovery.c |
| @@ -443,6 +443,27 @@ static int jbd2_commit_block_csum_verify(journal_t *j, void *buf) |
| return provided == cpu_to_be32(calculated); |
| } |
| |
| +static bool jbd2_commit_block_csum_verify_partial(journal_t *j, void *buf) |
| +{ |
| + struct commit_header *h; |
| + __be32 provided; |
| + __u32 calculated; |
| + void *tmpbuf; |
| + |
| + tmpbuf = kzalloc(j->j_blocksize, GFP_KERNEL); |
| + if (!tmpbuf) |
| + return false; |
| + |
| + memcpy(tmpbuf, buf, sizeof(struct commit_header)); |
| + h = tmpbuf; |
| + provided = h->h_chksum[0]; |
| + h->h_chksum[0] = 0; |
| + calculated = jbd2_chksum(j, j->j_csum_seed, tmpbuf, j->j_blocksize); |
| + kfree(tmpbuf); |
| + |
| + return provided == cpu_to_be32(calculated); |
| +} |
| + |
| static int jbd2_block_tag_csum_verify(journal_t *j, journal_block_tag_t *tag, |
| journal_block_tag3_t *tag3, |
| void *buf, __u32 sequence) |
| @@ -810,6 +831,13 @@ static int do_one_pass(journal_t *journal, |
| if (pass == PASS_SCAN && |
| !jbd2_commit_block_csum_verify(journal, |
| bh->b_data)) { |
| + if (jbd2_commit_block_csum_verify_partial( |
| + journal, |
| + bh->b_data)) { |
| + pr_notice("JBD2: Find incomplete commit block in transaction %u block %lu\n", |
| + next_commit_ID, next_log_block); |
| + goto chksum_ok; |
| + } |
| chksum_error: |
| if (commit_time < last_trans_commit_time) |
| goto ignore_crc_mismatch; |
| @@ -824,6 +852,7 @@ static int do_one_pass(journal_t *journal, |
| } |
| } |
| if (pass == PASS_SCAN) { |
| + chksum_ok: |
| last_trans_commit_time = commit_time; |
| head_block = next_log_block; |
| } |
| @@ -843,6 +872,7 @@ static int do_one_pass(journal_t *journal, |
| next_log_block); |
| need_check_commit_time = true; |
| } |
| + |
| /* If we aren't in the REVOKE pass, then we can |
| * just skip over this block. */ |
| if (pass != PASS_REVOKE) { |
| -- |
| 2.43.0 |
| |