ext4: make sure to revoke all the freeable blocks in ext4_free_blocks

Now, ext4_free_blocks() doesn't revoke data blocks of per-file data
journalled inode and it can cause file data inconsistency problems.
Even though data blocks of per-file data journalled inode are already
forgotten by jbd2_journal_invalidatepage() in advance of invoking
ext4_free_blocks(), we still need to revoke the data blocks here.
Moreover some of the metadata blocks, which are not found by
sb_find_get_block(), are still needed to be revoked, but this is also
missing here.

Signed-off-by: Daeho Jeong <daeho.jeong@samsung.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Jan Kara <jack@suse.cz>
This commit is contained in:
Daeho Jeong 2016-02-21 18:31:41 -05:00 committed by Theodore Ts'o
parent 74dae42785
commit f96c450dab

View File

@ -4694,16 +4694,6 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
inode, bh, block);
}
/*
* We need to make sure we don't reuse the freed block until
* after the transaction is committed, which we can do by
* treating the block as metadata, below. We make an
* exception if the inode is to be written in writeback mode
* since writeback mode has weak data consistency guarantees.
*/
if (!ext4_should_writeback_data(inode))
flags |= EXT4_FREE_BLOCKS_METADATA;
/*
* If the extent to be freed does not begin on a cluster
* boundary, we need to deal with partial clusters at the
@ -4738,14 +4728,13 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
if (!bh && (flags & EXT4_FREE_BLOCKS_FORGET)) {
int i;
int is_metadata = flags & EXT4_FREE_BLOCKS_METADATA;
for (i = 0; i < count; i++) {
cond_resched();
bh = sb_find_get_block(inode->i_sb, block + i);
if (!bh)
continue;
ext4_forget(handle, flags & EXT4_FREE_BLOCKS_METADATA,
inode, bh, block + i);
if (is_metadata)
bh = sb_find_get_block(inode->i_sb, block + i);
ext4_forget(handle, is_metadata, inode, bh, block + i);
}
}
@ -4819,12 +4808,17 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
if (err)
goto error_return;
if ((flags & EXT4_FREE_BLOCKS_METADATA) && ext4_handle_valid(handle)) {
/*
* We need to make sure we don't reuse the freed block until after the
* transaction is committed. We make an exception if the inode is to be
* written in writeback mode since writeback mode has weak data
* consistency guarantees.
*/
if (ext4_handle_valid(handle) &&
((flags & EXT4_FREE_BLOCKS_METADATA) ||
!ext4_should_writeback_data(inode))) {
struct ext4_free_data *new_entry;
/*
* blocks being freed are metadata. these blocks shouldn't
* be used until this transaction is committed
*
* We use __GFP_NOFAIL because ext4_free_blocks() is not allowed
* to fail.
*/