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

jbd: 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: Jan Kara <jack@suse.cz>

authored by

Yongqiang Yang and committed by
Jan Kara
8c111b3f 63894ab9

+41
+6
fs/jbd/commit.c
··· 392 392 jbd_debug (3, "JBD: commit phase 1\n"); 393 393 394 394 /* 395 + * Clear revoked flag to reflect there is no revoked buffers 396 + * in the next transaction which is going to be started. 397 + */ 398 + journal_clear_buffer_revoked_flags(journal); 399 + 400 + /* 395 401 * Switch to a new revoke table. 396 402 */ 397 403 journal_switch_revoke_table(journal);
+34
fs/jbd/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 ··· 481 477 } 482 478 } 483 479 return did_revoke; 480 + } 481 + 482 + /* 483 + * journal_clear_revoked_flags clears revoked flag of buffers in 484 + * revoke table to reflect there is no revoked buffer in the next 485 + * transaction which is going to be started. 486 + */ 487 + void journal_clear_buffer_revoked_flags(journal_t *journal) 488 + { 489 + struct jbd_revoke_table_s *revoke = journal->j_revoke; 490 + int i = 0; 491 + 492 + for (i = 0; i < revoke->hash_size; i++) { 493 + struct list_head *hash_list; 494 + struct list_head *list_entry; 495 + hash_list = &revoke->hash_table[i]; 496 + 497 + list_for_each(list_entry, hash_list) { 498 + struct jbd_revoke_record_s *record; 499 + struct buffer_head *bh; 500 + record = (struct jbd_revoke_record_s *)list_entry; 501 + bh = __find_get_block(journal->j_fs_dev, 502 + record->blocknr, 503 + journal->j_blocksize); 504 + if (bh) { 505 + clear_buffer_revoked(bh); 506 + __brelse(bh); 507 + } 508 + } 509 + } 484 510 } 485 511 486 512 /* journal_switch_revoke table select j_revoke for next transaction
+1
include/linux/jbd.h
··· 913 913 extern int journal_test_revoke(journal_t *, unsigned int, tid_t); 914 914 extern void journal_clear_revoke(journal_t *); 915 915 extern void journal_switch_revoke_table(journal_t *journal); 916 + extern void journal_clear_buffer_revoked_flags(journal_t *journal); 916 917 917 918 /* 918 919 * The log thread user interface: