From: Mingming Cao I found several bugs/issues in the ext2_new_inode() code: 1) The for loop variable "i" is used to save the inode offset. In the case of failure, the loop variable could be crapped. So it is possible to quit searching before looking at every block groups. 2) The number of free inodes in the selected group is possibly being miscalculated. The counter is only decreased in the find_group_xx() functions for the initial selected group. If the initial try failed, and succeed in finding a free inode in other group, the counter for that group will not to be decreased. 3) In case of the concurrent case, going back to find_group_xx() functions are unnecessary, it will only get the same group as before. The following patch fixed those issues. Ideas are stolen from ext3_new_inode(). 25-akpm/fs/ext2/ialloc.c | 41 ++++++++++++++++++++--------------------- 1 files changed, 20 insertions(+), 21 deletions(-) diff -puN fs/ext2/ialloc.c~ext2_new_inode-fixes fs/ext2/ialloc.c --- 25/fs/ext2/ialloc.c~ext2_new_inode-fixes Fri Nov 7 14:40:41 2003 +++ 25-akpm/fs/ext2/ialloc.c Fri Nov 7 14:40:41 2003 @@ -273,8 +273,6 @@ static int find_group_dir(struct super_b if (!best_desc) return -1; - ext2_reserve_inode(sb, best_group, 1); - return best_group; } @@ -419,7 +417,6 @@ fallback: return -1; found: - ext2_reserve_inode(sb, group, 1); return group; } @@ -481,8 +478,6 @@ static int find_group_other(struct super return -1; found: - ext2_reserve_inode(sb, group, 0); - return group; } @@ -528,12 +523,12 @@ repeat: bitmap_bh = read_inode_bitmap(sb, group); if (!bitmap_bh) { err = -EIO; - goto fail2; + goto fail; } - i = ext2_find_first_zero_bit((unsigned long *)bitmap_bh->b_data, + ino = ext2_find_first_zero_bit((unsigned long *)bitmap_bh->b_data, EXT2_INODES_PER_GROUP(sb)); - if (i >= EXT2_INODES_PER_GROUP(sb)) { + if (ino >= EXT2_INODES_PER_GROUP(sb)) { /* * Rare race: find_group_xx() decided that there were * free inodes in this group, but by the time we tried @@ -547,11 +542,10 @@ repeat: continue; } if (ext2_set_bit_atomic(sb_bgl_lock(EXT2_SB(sb), group), - i, bitmap_bh->b_data)) { - brelse(bitmap_bh); - bitmap_bh = NULL; - ext2_release_inode(sb, group, S_ISDIR(mode)); - goto repeat; + ino, bitmap_bh->b_data)) { + if (++group == sbi->s_groups_count) + group = 0; + continue; } goto got; } @@ -560,29 +554,35 @@ repeat: * Scanned all blockgroups. */ err = -ENOSPC; - goto fail2; + goto fail; got: mark_buffer_dirty(bitmap_bh); if (sb->s_flags & MS_SYNCHRONOUS) sync_dirty_buffer(bitmap_bh); brelse(bitmap_bh); - ino = group * EXT2_INODES_PER_GROUP(sb) + i + 1; + ino += group * EXT2_INODES_PER_GROUP(sb) + 1; if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) { ext2_error (sb, "ext2_new_inode", "reserved inode or inode > inodes count - " "block_group = %d,inode=%lu", group, (unsigned long) ino); err = -EIO; - goto fail2; + goto fail; } percpu_counter_mod(&EXT2_SB(sb)->s_freeinodes_counter, -1); + if (S_ISDIR(mode)) + percpu_counter_inc(&EXT2_SB(sb)->s_dirs_counter); spin_lock(sb_bgl_lock(EXT2_SB(sb), group)); + gdp->bg_free_inodes_count = + cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1); if (S_ISDIR(mode)) { if (EXT2_SB(sb)->s_debts[group] < 255) EXT2_SB(sb)->s_debts[group]++; + gdp->bg_used_dirs_count = + cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1); } else { if (EXT2_SB(sb)->s_debts[group]) EXT2_SB(sb)->s_debts[group]--; @@ -590,6 +590,7 @@ got: spin_unlock(sb_bgl_lock(EXT2_SB(sb), group)); sb->s_dirt = 1; + mark_buffer_dirty(bh2); inode->i_uid = current->fsuid; if (test_opt (sb, GRPID)) inode->i_gid = dir->i_gid; @@ -632,26 +633,24 @@ got: if (DQUOT_ALLOC_INODE(inode)) { DQUOT_DROP(inode); err = -ENOSPC; - goto fail3; + goto fail2; } err = ext2_init_acl(inode, dir); if (err) { DQUOT_FREE_INODE(inode); - goto fail3; + goto fail2; } mark_inode_dirty(inode); ext2_debug("allocating inode %lu\n", inode->i_ino); ext2_preread_inode(inode); return inode; -fail3: +fail2: inode->i_flags |= S_NOQUOTA; inode->i_nlink = 0; iput(inode); return ERR_PTR(err); -fail2: - ext2_release_inode(sb, group, S_ISDIR(mode)); fail: make_bad_inode(inode); iput(inode); _