Fix a couple of problems which were introduced by a recent race fix in the ext2 block allocator: - if the allocation attempt raced, and lost the race then a new attempt is made. But the earlier reservation must be put back first. Add a call to group_release_blocks() to fix this. - if the filesystem is genuinely corrupted then the code as-is can get stuck in an infinite loop, thinking that a blockgroup has free blocks and then discovering that its bitmap is full. Fix this by baling out after having scanned all blockgroups twice. (Thanks Muli Ben-Yehuda for spotting this). fs/ext2/balloc.c | 19 ++++++++++++++++--- 1 files changed, 16 insertions(+), 3 deletions(-) diff -puN fs/ext2/balloc.c~ext2-block-allocator-fixes fs/ext2/balloc.c --- 25/fs/ext2/balloc.c~ext2-block-allocator-fixes 2003-11-10 12:24:12.000000000 -0800 +++ 25-akpm/fs/ext2/balloc.c 2003-11-10 12:42:36.000000000 -0800 @@ -331,7 +331,7 @@ int ext2_new_block(struct inode *inode, struct ext2_group_desc *desc; int group_no; /* i */ int ret_block; /* j */ - int bit; /* k */ + int group_idx; /* k */ int target_block; /* tmp */ int block = 0; struct super_block *sb = inode->i_sb; @@ -340,6 +340,7 @@ int ext2_new_block(struct inode *inode, unsigned group_size = EXT2_BLOCKS_PER_GROUP(sb); unsigned prealloc_goal = es->s_prealloc_blocks; unsigned group_alloc = 0, es_alloc, dq_alloc; + int nr_scanned_groups; if (!prealloc_goal--) prealloc_goal = EXT2_DEFAULT_PREALLOC_BLOCKS - 1; @@ -402,9 +403,10 @@ int ext2_new_block(struct inode *inode, * Now search the rest of the groups. We assume that * i and desc correctly point to the last group visited. */ + nr_scanned_groups = 0; retry: - for (bit = 0; !group_alloc && - bit < sbi->s_groups_count; bit++) { + for (group_idx = 0; !group_alloc && + group_idx < sbi->s_groups_count; group_idx++) { group_no++; if (group_no >= sbi->s_groups_count) group_no = 0; @@ -427,9 +429,20 @@ retry: group_size, 0); if (ret_block < 0) { /* + * If a free block counter is corrupted we can loop inifintely. + * Detect that here. + */ + nr_scanned_groups++; + if (nr_scanned_groups > 2 * sbi->s_groups_count) { + ext2_error(sb, "ext2_new_block", + "corrupted free blocks counters"); + goto io_error; + } + /* * Someone else grabbed the last free block in this blockgroup * before us. Retry the scan. */ + group_release_blocks(sb, group_no, desc, gdp_bh, group_alloc); group_alloc = 0; goto retry; } _