From: Zhang Yi <[email protected]>
v1->v2:
- Remove the document about journal_cycle_record mount option.
Hello!
This is the e2fsprogs part of the ext4 journal cycled record
transactions option feature(corresponding kernel part is at [1]). It
add a new parameter to record the journal head of a clean filesystem,
mke2fs initialize it to the start of journal area, and e2fsck check and
fix it if it's bad, and also update it after recovering journal. Passed
functional tests.
Thanks,
Yi.
[1] https://lore.kernel.org/linux-ext4/[email protected]/
Zhang Yi (3):
lib/ext2fs: record and show journal head block
debugfs/e2fsck: update the journal head block after recovery
debugfs/e2fsck: check bad s_head block number
debugfs/journal.c | 10 +++++++++-
e2fsck/journal.c | 15 ++++++++++++++-
e2fsck/recovery.c | 21 +++++++++++++++++----
lib/e2p/ljs.c | 3 +++
lib/ext2fs/kernel-jbd.h | 6 ++++--
lib/ext2fs/mkjournal.c | 1 +
6 files changed, 48 insertions(+), 8 deletions(-)
--
2.31.1
From: Zhang Yi <[email protected]>
Add a new parameter into on-disk journal head block to record the head
block number of a clean journal image. This is used to support jbd2's
'JBD2_CYCLE_RECORD' option in kernel, which will be continue to record
new journal transactions between each mount, instead of always
recording from the first block. Note that the s_head is only uptodate
while the image is clean, we still need to walk through to find the head
block if the journal is not empty.
Signed-off-by: Zhang Yi <[email protected]>
---
lib/e2p/ljs.c | 3 +++
lib/ext2fs/kernel-jbd.h | 6 ++++--
lib/ext2fs/mkjournal.c | 1 +
3 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/lib/e2p/ljs.c b/lib/e2p/ljs.c
index 59728198..769f635a 100644
--- a/lib/e2p/ljs.c
+++ b/lib/e2p/ljs.c
@@ -108,6 +108,9 @@ void e2p_list_journal_super(FILE *f, char *journal_sb_buf,
"Journal start: %u\n",
(unsigned int)ntohl(jsb->s_sequence),
(unsigned int)ntohl(jsb->s_start));
+ if (ntohl(jsb->s_start) == 0)
+ fprintf(f, "Journal head: %u\n",
+ (unsigned int)ntohl(jsb->s_head));
if (nr_users != 1)
fprintf(f, "Journal number of users: %u\n", nr_users);
if (jsb->s_feature_compat & e2p_be32(JBD2_FEATURE_COMPAT_CHECKSUM))
diff --git a/lib/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
index e5695006..1a7857c0 100644
--- a/lib/ext2fs/kernel-jbd.h
+++ b/lib/ext2fs/kernel-jbd.h
@@ -221,8 +221,10 @@ typedef struct journal_superblock_s
__u8 s_padding2[3];
/* 0x0054 */
__be32 s_num_fc_blks; /* Number of fast commit blocks */
-/* 0x0058 */
- __be32 s_padding[41];
+ __be32 s_head; /* blocknr of head of log, only uptodate
+ * while the filesystem is clean */
+/* 0x005C */
+ __be32 s_padding[40];
__be32 s_checksum; /* crc32c(superblock) */
/* 0x0100 */
diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
index 54772dd5..24245bb7 100644
--- a/lib/ext2fs/mkjournal.c
+++ b/lib/ext2fs/mkjournal.c
@@ -79,6 +79,7 @@ errcode_t ext2fs_create_journal_superblock2(ext2_filsys fs,
jsb->s_nr_users = 0;
jsb->s_first = htonl(ext2fs_journal_sb_start(fs->blocksize) + 1);
}
+ jsb->s_head = jsb->s_first;
*ret_jsb = (char *) jsb;
return 0;
--
2.31.1
From: Zhang Yi <[email protected]>
The s_head parameter is not uptodate if the journal is not empty, so
we need to update it after recovery. We also reset it to the journal
first block if something wrong.
Signed-off-by: Zhang Yi <[email protected]>
---
debugfs/journal.c | 5 ++++-
e2fsck/journal.c | 6 +++++-
e2fsck/recovery.c | 21 +++++++++++++++++----
3 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/debugfs/journal.c b/debugfs/journal.c
index 5bac0d3b..5bc7552d 100644
--- a/debugfs/journal.c
+++ b/debugfs/journal.c
@@ -636,6 +636,7 @@ static errcode_t ext2fs_journal_load(journal_t *journal)
journal->j_tail = ntohl(jsb->s_start);
journal->j_first = ntohl(jsb->s_first);
journal->j_last = ntohl(jsb->s_maxlen);
+ journal->j_head = ntohl(jsb->s_head);
return 0;
}
@@ -650,8 +651,10 @@ static void ext2fs_journal_release(ext2_filsys fs, journal_t *journal,
else if (fs->flags & EXT2_FLAG_RW) {
jsb = journal->j_superblock;
jsb->s_sequence = htonl(journal->j_tail_sequence);
- if (reset)
+ if (reset) {
+ jsb->s_head = htonl(journal->j_head);
jsb->s_start = 0; /* this marks the journal as empty */
+ }
ext2fs_journal_sb_csum_set(journal, jsb);
mark_buffer_dirty(journal->j_sb_buffer);
}
diff --git a/e2fsck/journal.c b/e2fsck/journal.c
index c7868d89..8950446f 100644
--- a/e2fsck/journal.c
+++ b/e2fsck/journal.c
@@ -1378,6 +1378,7 @@ static errcode_t e2fsck_journal_load(journal_t *journal)
journal->j_transaction_sequence = journal->j_tail_sequence;
journal->j_tail = ntohl(jsb->s_start);
journal->j_first = ntohl(jsb->s_first);
+ journal->j_head = ntohl(jsb->s_head);
if (jbd2_has_feature_fast_commit(journal)) {
if (ntohl(jsb->s_maxlen) - jbd2_journal_get_num_fc_blks(jsb)
< JBD2_MIN_JOURNAL_BLOCKS) {
@@ -1426,6 +1427,7 @@ static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
jsb->s_blocksize = htonl(ctx->fs->blocksize);
jsb->s_maxlen = htonl(journal->j_total_len);
jsb->s_first = htonl(1);
+ jsb->s_head = jsb->s_first;
/* Initialize the journal sequence number so that there is "no"
* chance we will find old "valid" transactions in the journal.
@@ -1474,8 +1476,10 @@ static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
else if (!(ctx->options & E2F_OPT_READONLY)) {
jsb = journal->j_superblock;
jsb->s_sequence = htonl(journal->j_tail_sequence);
- if (reset)
+ if (reset) {
+ jsb->s_head = htonl(journal->j_head);
jsb->s_start = 0; /* this marks the journal as empty */
+ }
e2fsck_journal_sb_csum_set(journal, jsb);
mark_buffer_dirty(journal->j_sb_buffer);
}
diff --git a/e2fsck/recovery.c b/e2fsck/recovery.c
index 8ca35271..ea3a7646 100644
--- a/e2fsck/recovery.c
+++ b/e2fsck/recovery.c
@@ -29,6 +29,7 @@ struct recovery_info
{
tid_t start_transaction;
tid_t end_transaction;
+ unsigned long head_block;
int nr_replays;
int nr_revokes;
@@ -297,9 +298,10 @@ int jbd2_journal_recover(journal_t *journal)
*/
if (!sb->s_start) {
- jbd_debug(1, "No recovery required, last transaction %d\n",
- be32_to_cpu(sb->s_sequence));
+ jbd_debug(1, "No recovery required, last transaction %d, head block %u\n",
+ be32_to_cpu(sb->s_sequence), be32_to_cpu(sb->s_head));
journal->j_transaction_sequence = be32_to_cpu(sb->s_sequence) + 1;
+ journal->j_head = be32_to_cpu(sb->s_head);
return 0;
}
@@ -318,6 +320,9 @@ int jbd2_journal_recover(journal_t *journal)
/* Restart the log at the next transaction ID, thus invalidating
* any existing commit records in the log. */
journal->j_transaction_sequence = ++info.end_transaction;
+ journal->j_head = info.head_block;
+ jbd_debug(1, "JBD2: last transaction %d, head block %lu\n",
+ journal->j_transaction_sequence, journal->j_head);
jbd2_journal_clear_revoke(journal);
err2 = sync_blockdev(journal->j_fs_dev);
@@ -358,6 +363,7 @@ int jbd2_journal_skip_recovery(journal_t *journal)
if (err) {
printk(KERN_ERR "JBD2: error %d scanning journal\n", err);
++journal->j_transaction_sequence;
+ journal->j_head = journal->j_first;
} else {
#ifdef CONFIG_JBD2_DEBUG
int dropped = info.end_transaction -
@@ -367,6 +373,7 @@ int jbd2_journal_skip_recovery(journal_t *journal)
dropped, (dropped == 1) ? "" : "s");
#endif
journal->j_transaction_sequence = ++info.end_transaction;
+ journal->j_head = info.head_block;
}
journal->j_tail = 0;
@@ -456,7 +463,7 @@ static int do_one_pass(journal_t *journal,
struct recovery_info *info, enum passtype pass)
{
unsigned int first_commit_ID, next_commit_ID;
- unsigned long next_log_block;
+ unsigned long next_log_block, head_block;
int err, success = 0;
journal_superblock_t * sb;
journal_header_t * tmp;
@@ -479,6 +486,7 @@ static int do_one_pass(journal_t *journal,
sb = journal->j_superblock;
next_commit_ID = be32_to_cpu(sb->s_sequence);
next_log_block = be32_to_cpu(sb->s_start);
+ head_block = next_log_block;
first_commit_ID = next_commit_ID;
if (pass == PASS_SCAN)
@@ -804,6 +812,7 @@ static int do_one_pass(journal_t *journal,
if (commit_time < last_trans_commit_time)
goto ignore_crc_mismatch;
info->end_transaction = next_commit_ID;
+ info->head_block = head_block;
if (!jbd2_has_feature_async_commit(journal)) {
journal->j_failed_commit =
@@ -812,8 +821,10 @@ static int do_one_pass(journal_t *journal,
break;
}
}
- if (pass == PASS_SCAN)
+ if (pass == PASS_SCAN) {
last_trans_commit_time = commit_time;
+ head_block = next_log_block;
+ }
brelse(bh);
next_commit_ID++;
continue;
@@ -863,6 +874,8 @@ static int do_one_pass(journal_t *journal,
if (pass == PASS_SCAN) {
if (!info->end_transaction)
info->end_transaction = next_commit_ID;
+ if (!info->head_block)
+ info->head_block = head_block;
} else {
/* It's really bad news if different passes end up at
* different places (but possible due to IO errors). */
--
2.31.1
From: Zhang Yi <[email protected]>
Check s_head in the journal superblock and fix it if this value is out
of bounds.
Signed-off-by: Zhang Yi <[email protected]>
---
debugfs/journal.c | 5 +++++
e2fsck/journal.c | 9 +++++++++
2 files changed, 14 insertions(+)
diff --git a/debugfs/journal.c b/debugfs/journal.c
index 5bc7552d..1eef3bca 100644
--- a/debugfs/journal.c
+++ b/debugfs/journal.c
@@ -631,6 +631,11 @@ static errcode_t ext2fs_journal_load(journal_t *journal)
else if (ntohl(jsb->s_maxlen) > journal->j_total_len)
return EXT2_ET_CORRUPT_JOURNAL_SB;
+ if (jsb->s_head != 0 &&
+ (ntohl(jsb->s_head) < ntohl(jsb->s_first) ||
+ ntohl(jsb->s_head) >= journal->j_total_len))
+ return EXT2_ET_CORRUPT_JOURNAL_SB;
+
journal->j_tail_sequence = ntohl(jsb->s_sequence);
journal->j_transaction_sequence = journal->j_tail_sequence;
journal->j_tail = ntohl(jsb->s_start);
diff --git a/e2fsck/journal.c b/e2fsck/journal.c
index 8950446f..4b9f00ce 100644
--- a/e2fsck/journal.c
+++ b/e2fsck/journal.c
@@ -1374,6 +1374,15 @@ static errcode_t e2fsck_journal_load(journal_t *journal)
return EXT2_ET_CORRUPT_JOURNAL_SB;
}
+ if (jsb->s_head != 0 &&
+ (ntohl(jsb->s_head) < ntohl(jsb->s_first) ||
+ ntohl(jsb->s_head) >= journal->j_total_len)) {
+ com_err(ctx->program_name, EXT2_ET_CORRUPT_JOURNAL_SB,
+ _("%s, journal head out of bounds\n"),
+ ctx->device_name);
+ return EXT2_ET_CORRUPT_JOURNAL_SB;
+ }
+
journal->j_tail_sequence = ntohl(jsb->s_sequence);
journal->j_transaction_sequence = journal->j_tail_sequence;
journal->j_tail = ntohl(jsb->s_start);
--
2.31.1