ext4: Check that reservation counters are self-consistent. Signed-off-by: Dmitry Monakhov diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 49d88c0..2b072c9 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "ext4.h" #include "ext4_jbd2.h" @@ -640,6 +641,18 @@ static void dump_orphan_list(struct super_block *sb, struct ext4_sb_info *sbi) } } +#define CHECK_COUNTER(sb, counter, val) \ + do { \ + if (percpu_counter_sum_positive(&EXT4_SB(sb)->counter) != \ + (s64)(val)) \ + ext4_warning((sb), "Counter %s contain unexpected " \ + "value %lld, expected %lld\n", \ + __stringify(counter), \ + percpu_counter_sum_positive( \ + &EXT4_SB(sb)->counter), \ + (s64)(val)); \ + } while(0); + static void ext4_put_super(struct super_block *sb) { struct ext4_sb_info *sbi = EXT4_SB(sb); @@ -677,6 +690,11 @@ static void ext4_put_super(struct super_block *sb) } kobject_del(&sbi->s_kobj); + CHECK_COUNTER(sb, s_dirs_counter, ext4_count_dirs(sb)); + CHECK_COUNTER(sb, s_dirtyblocks_counter, 0UL); + CHECK_COUNTER(sb, s_freeblocks_counter, ext4_count_free_blocks(sb)); + CHECK_COUNTER(sb, s_freeinodes_counter, ext4_count_free_inodes(sb)); + for (i = 0; i < sbi->s_gdb_count; i++) brelse(sbi->s_group_desc[i]); kfree(sbi->s_group_desc); @@ -770,7 +788,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) static void ext4_destroy_inode(struct inode *inode) { - if (!list_empty(&(EXT4_I(inode)->i_orphan))) { + if (unlikely(!list_empty(&(EXT4_I(inode)->i_orphan)))) { ext4_msg(inode->i_sb, KERN_ERR, "Inode %lu (%p): orphan list check failed!", inode->i_ino, EXT4_I(inode)); @@ -779,6 +797,18 @@ static void ext4_destroy_inode(struct inode *inode) true); dump_stack(); } + if (unlikely(EXT4_I(inode)->i_reserved_data_blocks) || + EXT4_I(inode)->i_reserved_meta_blocks || + EXT4_I(inode)->i_allocated_meta_blocks) { + ext4_msg(inode->i_sb, KERN_ERR, "Inode %lu (%p): Destroyed" + " inode still has reserved blocks and probably would" + " leak, rsv_data=%u, rsv_mdata=%u, alloc_mblk=%u", + inode->i_ino, EXT4_I(inode), + EXT4_I(inode)->i_reserved_data_blocks, + EXT4_I(inode)->i_reserved_meta_blocks, + EXT4_I(inode)->i_allocated_meta_blocks); + dump_stack(); + } kmem_cache_free(ext4_inode_cachep, EXT4_I(inode)); }