Skip to content

Commit

Permalink
jbd2: avoid mount failed when commit block is partial submitted
Browse files Browse the repository at this point in the history
[ Upstream commit 0bab8db ]

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 <[email protected]>
Reviewed-by: Jan Kara <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
  • Loading branch information
Ye Bin authored and gregkh committed Sep 12, 2024
1 parent 3236afd commit e16c4c2
Showing 1 changed file with 30 additions and 0 deletions.
30 changes: 30 additions & 0 deletions fs/jbd2/recovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,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)
Expand Down Expand Up @@ -814,6 +835,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;
Expand All @@ -828,6 +856,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;
}
Expand All @@ -847,6 +876,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) {
Expand Down

0 comments on commit e16c4c2

Please sign in to comment.