Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

jbd2: clear revoked flag on buffers before a new transaction started

Currently, we clear revoked flag only when a block is reused. However,
this can tigger a false journal error. Consider a situation when a block
is used as a meta block and is deleted(revoked) in ordered mode, then the
block is allocated as a data block to a file. At this moment, user changes
the file's journal mode from ordered to journaled and truncates the file.
The block will be considered re-revoked by journal because it has revoked
flag still pending from the last transaction and an assertion triggers.

We fix the problem by keeping the revoked status more uptodate - we clear
revoked flag when switching revoke tables to reflect there is no revoked
buffers in current transaction any more.

Signed-off-by: Yongqiang Yang <xiaoqiangnk@gmail.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>

authored by

Yongqiang Yang and committed by
Theodore Ts'o
1ba37268 5872ddaa

+41
+6
fs/jbd2/commit.c
··· 430 430 jbd_debug(3, "JBD2: commit phase 1\n"); 431 431 432 432 /* 433 + * Clear revoked flag to reflect there is no revoked buffers 434 + * in the next transaction which is going to be started. 435 + */ 436 + jbd2_clear_buffer_revoked_flags(journal); 437 + 438 + /* 433 439 * Switch to a new revoke table. 434 440 */ 435 441 jbd2_journal_switch_revoke_table(journal);
+34
fs/jbd2/revoke.c
··· 47 47 * overwriting the new data. We don't even need to clear the revoke 48 48 * bit here. 49 49 * 50 + * We cache revoke status of a buffer in the current transaction in b_states 51 + * bits. As the name says, revokevalid flag indicates that the cached revoke 52 + * status of a buffer is valid and we can rely on the cached status. 53 + * 50 54 * Revoke information on buffers is a tri-state value: 51 55 * 52 56 * RevokeValid clear: no cached revoke status, need to look it up ··· 480 476 } 481 477 } 482 478 return did_revoke; 479 + } 480 + 481 + /* 482 + * journal_clear_revoked_flag clears revoked flag of buffers in 483 + * revoke table to reflect there is no revoked buffers in the next 484 + * transaction which is going to be started. 485 + */ 486 + void jbd2_clear_buffer_revoked_flags(journal_t *journal) 487 + { 488 + struct jbd2_revoke_table_s *revoke = journal->j_revoke; 489 + int i = 0; 490 + 491 + for (i = 0; i < revoke->hash_size; i++) { 492 + struct list_head *hash_list; 493 + struct list_head *list_entry; 494 + hash_list = &revoke->hash_table[i]; 495 + 496 + list_for_each(list_entry, hash_list) { 497 + struct jbd2_revoke_record_s *record; 498 + struct buffer_head *bh; 499 + record = (struct jbd2_revoke_record_s *)list_entry; 500 + bh = __find_get_block(journal->j_fs_dev, 501 + record->blocknr, 502 + journal->j_blocksize); 503 + if (bh) { 504 + clear_buffer_revoked(bh); 505 + __brelse(bh); 506 + } 507 + } 508 + } 483 509 } 484 510 485 511 /* journal_switch_revoke table select j_revoke for next transaction
+1
include/linux/jbd2.h
··· 1151 1151 extern int jbd2_journal_test_revoke(journal_t *, unsigned long long, tid_t); 1152 1152 extern void jbd2_journal_clear_revoke(journal_t *); 1153 1153 extern void jbd2_journal_switch_revoke_table(journal_t *journal); 1154 + extern void jbd2_clear_buffer_revoked_flags(journal_t *journal); 1154 1155 1155 1156 /* 1156 1157 * The log thread user interface: