From c896e0bac91bf42923d71749ae2a55046a3d88b8 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 10 Feb 2019 23:32:39 -0500 Subject: [PATCH] add patch discard-dirty-data-when-forgetting-unjournaled-buffer --- ...d-dirty-data-when-forgetting-unjournaled-buffer | 105 +++++++++++++++++++++ series | 1 + timestamps | 7 +- 3 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 discard-dirty-data-when-forgetting-unjournaled-buffer diff --git a/discard-dirty-data-when-forgetting-unjournaled-buffer b/discard-dirty-data-when-forgetting-unjournaled-buffer new file mode 100644 index 00000000..f5a59704 --- /dev/null +++ b/discard-dirty-data-when-forgetting-unjournaled-buffer @@ -0,0 +1,105 @@ +jbd2: discard dirty data when forgetting an un-journalled buffer + +From: "zhangyi (F)" + +We do not unmap and clear dirty flag when forgetting a buffer without +journal or does not belongs to any transaction, so the invalid dirty +data may still be written to the disk later. It's fine if the +corresponding block is never used before the next mount, and it's also +fine that we invoke clean_bdev_aliases() related functions to unmap +the block device mapping when re-allocating such freed block as data +block. But this logic is somewhat fragile and risky that may lead to +data corruption if we forget to clean bdev aliases. So, It's better to +discard dirty data during forget time. + +We have been already handled all the cases of forgetting journalled +buffer, this patch deal with the remaining two cases. + +- buffer is not journalled yet, +- buffer is journalled but doesn't belongs to any transaction. + +We invoke __bforget() instead of __brelese() when forgetting an +un-journalled buffer in jbd2_journal_forget(). After this patch we can +remove all clean_bdev_aliases() related calls in ext4. + +Suggested-by: Jan Kara +Signed-off-by: zhangyi (F) +Signed-off-by: Theodore Ts'o +Reviewed-by: Jan Kara +--- + fs/jbd2/transaction.c | 42 ++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 38 insertions(+), 4 deletions(-) + +diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c +index f0d8dab..a43b630 100644 +--- a/fs/jbd2/transaction.c ++++ b/fs/jbd2/transaction.c +@@ -1597,9 +1597,7 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) + __jbd2_journal_unfile_buffer(jh); + if (!buffer_jbd(bh)) { + spin_unlock(&journal->j_list_lock); +- jbd_unlock_bh_state(bh); +- __bforget(bh); +- goto drop; ++ goto not_jbd; + } + } + spin_unlock(&journal->j_list_lock); +@@ -1632,9 +1630,40 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) + if (was_modified) + drop_reserve = 1; + } ++ } else { ++ /* ++ * Finally, if the buffer is not belongs to any ++ * transaction, we can just drop it now if it has no ++ * checkpoint. ++ */ ++ spin_lock(&journal->j_list_lock); ++ if (!jh->b_cp_transaction) { ++ JBUFFER_TRACE(jh, "belongs to none transaction"); ++ spin_unlock(&journal->j_list_lock); ++ goto not_jbd; ++ } ++ ++ /* ++ * Otherwise, if the buffer has been written to disk, ++ * it is safe to remove the checkpoint and drop it. ++ */ ++ if (!buffer_dirty(bh)) { ++ __jbd2_journal_remove_checkpoint(jh); ++ spin_unlock(&journal->j_list_lock); ++ goto not_jbd; ++ } ++ ++ /* ++ * The buffer is still not written to disk, we should ++ * attach this buffer to current transaction so that the ++ * buffer can be checkpointed only after the current ++ * transaction commits. ++ */ ++ clear_buffer_dirty(bh); ++ __jbd2_journal_file_buffer(jh, transaction, BJ_Forget); ++ spin_unlock(&journal->j_list_lock); + } + +-not_jbd: + jbd_unlock_bh_state(bh); + __brelse(bh); + drop: +@@ -1643,6 +1672,11 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) + handle->h_buffer_credits++; + } + return err; ++ ++not_jbd: ++ jbd_unlock_bh_state(bh); ++ __bforget(bh); ++ goto drop; + } + + /** +-- +2.7.4 + + diff --git a/series b/series index 9f1b070c..c21c602e 100644 --- a/series +++ b/series @@ -4,6 +4,7 @@ revert-use-write_inode_for-fsyncing-wo-journal fix-deadlock-while-checkpoint-thread-waits-commit-thread-to-finish replace-open-i_writecount-usage clear-dirty-flag-when-revoking-buffer-from-older-txn +discard-dirty-data-when-forgetting-unjournaled-buffer #fix-slow-writeback-under-dioread_nolock-and-nodelalloc diff --git a/timestamps b/timestamps index c8e7ab51..e6773b6c 100755 --- a/timestamps +++ b/timestamps @@ -19,7 +19,8 @@ touch -d @1546313731 stable-boundary touch -d @1548996071 revert-use-write_inode_for-fsyncing-wo-journal touch -d @1548996131 fix-deadlock-while-checkpoint-thread-waits-commit-thread-to-finish touch -d @1549857856 replace-open-i_writecount-usage -touch -d @1549858664 series touch -d @1549858984 clear-dirty-flag-when-revoking-buffer-from-older-txn -touch -d @1549859022 status -touch -d @1549859087 timestamps +touch -d @1549859166 discard-dirty-data-when-forgetting-unjournaled-buffer +touch -d @1549859184 series +touch -d @1549859191 status +touch -d @1549859551 timestamps -- 2.11.4.GIT