Commit cd213226 authored by Mingming Cao's avatar Mingming Cao Committed by Theodore Ts'o

ext4: Fix delalloc release block reservation for truncate

Ext4 will release the reserved blocks for delayed allocations when
inode is truncated/unlinked.  If there is no reserved block at all, we
shouldn't need to do so.  But current code still tries to release the
reserved blocks regardless whether the counters's value is 0.
Continue to do that causes the later calculation to go wrong and a
kernel BUG_ON() caught that. This doesn't happen for extent-based
files, as the calculation for 0 reserved blocks was right for extent
based file.

This patch fixed the kernel BUG() due to above reason.  It adds checks
for 0 to avoid unnecessary release and fix calculation for non-extent
files.
Signed-off-by: default avatarMingming Cao <cmm@us.ibm.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent b4df2030
...@@ -1005,6 +1005,9 @@ static int ext4_indirect_calc_metadata_amount(struct inode *inode, int blocks) ...@@ -1005,6 +1005,9 @@ static int ext4_indirect_calc_metadata_amount(struct inode *inode, int blocks)
*/ */
static int ext4_calc_metadata_amount(struct inode *inode, int blocks) static int ext4_calc_metadata_amount(struct inode *inode, int blocks)
{ {
if (!blocks)
return 0;
if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)
return ext4_ext_calc_metadata_amount(inode, blocks); return ext4_ext_calc_metadata_amount(inode, blocks);
...@@ -1559,7 +1562,25 @@ static void ext4_da_release_space(struct inode *inode, int to_free) ...@@ -1559,7 +1562,25 @@ static void ext4_da_release_space(struct inode *inode, int to_free)
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
int total, mdb, mdb_free, release; int total, mdb, mdb_free, release;
if (!to_free)
return; /* Nothing to release, exit */
spin_lock(&EXT4_I(inode)->i_block_reservation_lock); spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
if (!EXT4_I(inode)->i_reserved_data_blocks) {
/*
* if there is no reserved blocks, but we try to free some
* then the counter is messed up somewhere.
* but since this function is called from invalidate
* page, it's harmless to return without any action
*/
printk(KERN_INFO "ext4 delalloc try to release %d reserved "
"blocks for inode %lu, but there is no reserved "
"data blocks\n", to_free, inode->i_ino);
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
return;
}
/* recalculate the number of metablocks still need to be reserved */ /* recalculate the number of metablocks still need to be reserved */
total = EXT4_I(inode)->i_reserved_data_blocks - to_free; total = EXT4_I(inode)->i_reserved_data_blocks - to_free;
mdb = ext4_calc_metadata_amount(inode, total); mdb = ext4_calc_metadata_amount(inode, total);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment