Skip to content

Commit c7fc605

Browse files
zhangyi089tytso
authored andcommitted
jbd2: continue to record log between each mount
For a newly mounted file system, the journal committing thread always record new transactions from the start of the journal area, no matter whether the journal was clean or just has been recovered. So the logdump code in debugfs cannot dump continuous logs between each mount, it is disadvantageous to analysis corrupted file system image and locate the file system inconsistency bugs. If we get a corrupted file system in the running products and want to find out what has happened, besides lookup the system log, one effective way is to backtrack the journal log. But we may not always run e2fsck before each mount and the default fsck -a mode also cannot always checkout all inconsistencies, so it could left over some inconsistencies into the next mount until we detect it. Finally, transactions in the journal may probably discontinuous and some relatively new transactions has been covered, it becomes hard to analyse. If we could record transactions continuously between each mount, we could acquire more useful info from the journal. Like this: |Previous mount checkpointed/recovered logs|Current mount logs | |{------}{---}{--------} ... {------}| ... |{======}{========}...000000| And yes the journal area is limited and cannot record everything, the problematic transaction may also be covered even if we do this, but this is still useful for fuzzy tests and short-running products. This patch save the head blocknr in the superblock after flushing the journal or unmounting the file system, let the next mount could continue to record new transaction behind it. This change is backward compatible because the old kernel does not care about the head blocknr of the journal. It is also fine if we mount a clean old image without valid head blocknr, we fail back to set it to s_first just like before. Finally, for the case of mount an unclean file system, we could also get the journal head easily after scanning/replaying the journal, it will continue to record new transaction after the recovered transactions. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20230322013353.1843306-2-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
1 parent 04c2e98 commit c7fc605

3 files changed

Lines changed: 40 additions & 9 deletions

File tree

fs/jbd2/journal.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,8 +1559,21 @@ static int journal_reset(journal_t *journal)
15591559
journal->j_first = first;
15601560
journal->j_last = last;
15611561

1562-
journal->j_head = journal->j_first;
1563-
journal->j_tail = journal->j_first;
1562+
if (journal->j_head != 0 && journal->j_flags & JBD2_CYCLE_RECORD) {
1563+
/*
1564+
* Disable the cycled recording mode if the journal head block
1565+
* number is not correct.
1566+
*/
1567+
if (journal->j_head < first || journal->j_head >= last) {
1568+
printk(KERN_WARNING "JBD2: Incorrect Journal head block %lu, "
1569+
"disable journal_cycle_record\n",
1570+
journal->j_head);
1571+
journal->j_head = journal->j_first;
1572+
}
1573+
} else {
1574+
journal->j_head = journal->j_first;
1575+
}
1576+
journal->j_tail = journal->j_head;
15641577
journal->j_free = journal->j_last - journal->j_first;
15651578

15661579
journal->j_tail_sequence = journal->j_transaction_sequence;
@@ -1732,6 +1745,7 @@ static void jbd2_mark_journal_empty(journal_t *journal, blk_opf_t write_flags)
17321745

17331746
sb->s_sequence = cpu_to_be32(journal->j_tail_sequence);
17341747
sb->s_start = cpu_to_be32(0);
1748+
sb->s_head = cpu_to_be32(journal->j_head);
17351749
if (jbd2_has_feature_fast_commit(journal)) {
17361750
/*
17371751
* When journal is clean, no need to commit fast commit flag and

fs/jbd2/recovery.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct recovery_info
2929
{
3030
tid_t start_transaction;
3131
tid_t end_transaction;
32+
unsigned long head_block;
3233

3334
int nr_replays;
3435
int nr_revokes;
@@ -301,11 +302,11 @@ int jbd2_journal_recover(journal_t *journal)
301302
* is always zero if, and only if, the journal was cleanly
302303
* unmounted.
303304
*/
304-
305305
if (!sb->s_start) {
306-
jbd2_debug(1, "No recovery required, last transaction %d\n",
307-
be32_to_cpu(sb->s_sequence));
306+
jbd2_debug(1, "No recovery required, last transaction %d, head block %u\n",
307+
be32_to_cpu(sb->s_sequence), be32_to_cpu(sb->s_head));
308308
journal->j_transaction_sequence = be32_to_cpu(sb->s_sequence) + 1;
309+
journal->j_head = be32_to_cpu(sb->s_head);
309310
return 0;
310311
}
311312

@@ -324,6 +325,9 @@ int jbd2_journal_recover(journal_t *journal)
324325
/* Restart the log at the next transaction ID, thus invalidating
325326
* any existing commit records in the log. */
326327
journal->j_transaction_sequence = ++info.end_transaction;
328+
journal->j_head = info.head_block;
329+
jbd2_debug(1, "JBD2: last transaction %d, head block %lu\n",
330+
journal->j_transaction_sequence, journal->j_head);
327331

328332
jbd2_journal_clear_revoke(journal);
329333
err2 = sync_blockdev(journal->j_fs_dev);
@@ -364,6 +368,7 @@ int jbd2_journal_skip_recovery(journal_t *journal)
364368
if (err) {
365369
printk(KERN_ERR "JBD2: error %d scanning journal\n", err);
366370
++journal->j_transaction_sequence;
371+
journal->j_head = journal->j_first;
367372
} else {
368373
#ifdef CONFIG_JBD2_DEBUG
369374
int dropped = info.end_transaction -
@@ -373,6 +378,7 @@ int jbd2_journal_skip_recovery(journal_t *journal)
373378
dropped, (dropped == 1) ? "" : "s");
374379
#endif
375380
journal->j_transaction_sequence = ++info.end_transaction;
381+
journal->j_head = info.head_block;
376382
}
377383

378384
journal->j_tail = 0;
@@ -462,7 +468,7 @@ static int do_one_pass(journal_t *journal,
462468
struct recovery_info *info, enum passtype pass)
463469
{
464470
unsigned int first_commit_ID, next_commit_ID;
465-
unsigned long next_log_block;
471+
unsigned long next_log_block, head_block;
466472
int err, success = 0;
467473
journal_superblock_t * sb;
468474
journal_header_t * tmp;
@@ -485,6 +491,7 @@ static int do_one_pass(journal_t *journal,
485491
sb = journal->j_superblock;
486492
next_commit_ID = be32_to_cpu(sb->s_sequence);
487493
next_log_block = be32_to_cpu(sb->s_start);
494+
head_block = next_log_block;
488495

489496
first_commit_ID = next_commit_ID;
490497
if (pass == PASS_SCAN)
@@ -809,6 +816,7 @@ static int do_one_pass(journal_t *journal,
809816
if (commit_time < last_trans_commit_time)
810817
goto ignore_crc_mismatch;
811818
info->end_transaction = next_commit_ID;
819+
info->head_block = head_block;
812820

813821
if (!jbd2_has_feature_async_commit(journal)) {
814822
journal->j_failed_commit =
@@ -817,8 +825,10 @@ static int do_one_pass(journal_t *journal,
817825
break;
818826
}
819827
}
820-
if (pass == PASS_SCAN)
828+
if (pass == PASS_SCAN) {
821829
last_trans_commit_time = commit_time;
830+
head_block = next_log_block;
831+
}
822832
brelse(bh);
823833
next_commit_ID++;
824834
continue;
@@ -868,6 +878,8 @@ static int do_one_pass(journal_t *journal,
868878
if (pass == PASS_SCAN) {
869879
if (!info->end_transaction)
870880
info->end_transaction = next_commit_ID;
881+
if (!info->head_block)
882+
info->head_block = head_block;
871883
} else {
872884
/* It's really bad news if different passes end up at
873885
* different places (but possible due to IO errors). */

include/linux/jbd2.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,10 @@ typedef struct journal_superblock_s
265265
__u8 s_padding2[3];
266266
/* 0x0054 */
267267
__be32 s_num_fc_blks; /* Number of fast commit blocks */
268-
/* 0x0058 */
269-
__u32 s_padding[41];
268+
__be32 s_head; /* blocknr of head of log, only uptodate
269+
* while the filesystem is clean */
270+
/* 0x005C */
271+
__u32 s_padding[40];
270272
__be32 s_checksum; /* crc32c(superblock) */
271273

272274
/* 0x0100 */
@@ -1395,6 +1397,9 @@ JBD2_FEATURE_INCOMPAT_FUNCS(fast_commit, FAST_COMMIT)
13951397
#define JBD2_ABORT_ON_SYNCDATA_ERR 0x040 /* Abort the journal on file
13961398
* data write error in ordered
13971399
* mode */
1400+
#define JBD2_CYCLE_RECORD 0x080 /* Journal cycled record log on
1401+
* clean and empty filesystem
1402+
* logging area */
13981403
#define JBD2_FAST_COMMIT_ONGOING 0x100 /* Fast commit is ongoing */
13991404
#define JBD2_FULL_COMMIT_ONGOING 0x200 /* Full commit is ongoing */
14001405
#define JBD2_JOURNAL_FLUSH_DISCARD 0x0001

0 commit comments

Comments
 (0)