From Alex Tomas. Fix a lockup wherein the inode allocation code will loop around thinking that a blockgroup has a free block, then finding that it didn't, then reselecting the same blockgroup. fs/ext3/ialloc.c | 84 +++++++++++++++++++++++++++++-------------------------- 1 files changed, 45 insertions(+), 39 deletions(-) diff -puN fs/ext3/ialloc.c~ext3-concurrent-block-inode-allocation-fix fs/ext3/ialloc.c --- 25/fs/ext3/ialloc.c~ext3-concurrent-block-inode-allocation-fix 2003-05-29 10:48:00.000000000 -0700 +++ 25-akpm/fs/ext3/ialloc.c 2003-05-29 11:39:15.000000000 -0700 @@ -426,14 +426,15 @@ struct inode *ext3_new_inode(handle_t *h struct buffer_head *bitmap_bh = NULL; struct buffer_head *bh2; int group; - unsigned long ino; + unsigned long ino = 0; struct inode * inode; - struct ext3_group_desc * gdp; + struct ext3_group_desc * gdp = NULL; struct ext3_super_block * es; struct ext3_inode_info *ei; struct ext3_sb_info *sbi; int err = 0; struct inode *ret; + int i; /* Cannot create files in a deleted directory */ if (!dir || !dir->i_nlink) @@ -447,7 +448,6 @@ struct inode *ext3_new_inode(handle_t *h es = EXT3_SB(sb)->s_es; sbi = EXT3_SB(sb); -repeat: if (S_ISDIR(mode)) { if (test_opt (sb, OLDALLOC)) group = find_group_dir(sb, dir); @@ -460,46 +460,52 @@ repeat: if (group == -1) goto out; - err = -EIO; - brelse(bitmap_bh); - bitmap_bh = read_inode_bitmap(sb, group); - if (!bitmap_bh) - goto fail; - gdp = ext3_get_group_desc (sb, group, &bh2); - - if ((ino = ext3_find_first_zero_bit((unsigned long *)bitmap_bh->b_data, - EXT3_INODES_PER_GROUP(sb))) < - EXT3_INODES_PER_GROUP(sb)) { - BUFFER_TRACE(bitmap_bh, "get_write_access"); - err = ext3_journal_get_write_access(handle, bitmap_bh); - if (err) goto fail; - - if (ext3_set_bit_atomic(sb_bgl_lock(sbi, group), - ino, bitmap_bh->b_data)) - goto repeat; - BUFFER_TRACE(bitmap_bh, "call ext3_journal_dirty_metadata"); - err = ext3_journal_dirty_metadata(handle, bitmap_bh); - if (err) goto fail; - } else { - if (le16_to_cpu(gdp->bg_free_inodes_count) != 0) { - ext3_error (sb, "ext3_new_inode", - "Free inodes count corrupted in group %d", - group); - /* Is it really ENOSPC? */ - err = -ENOSPC; - if (sb->s_flags & MS_RDONLY) + for (i = 0; i < sbi->s_groups_count; i++) { + gdp = ext3_get_group_desc(sb, group, &bh2); + + err = -EIO; + brelse(bitmap_bh); + bitmap_bh = read_inode_bitmap(sb, group); + if (!bitmap_bh) + goto fail; + + ino = ext3_find_first_zero_bit((unsigned long *) + bitmap_bh->b_data, EXT3_INODES_PER_GROUP(sb)); + if (ino < EXT3_INODES_PER_GROUP(sb)) { + BUFFER_TRACE(bitmap_bh, "get_write_access"); + err = ext3_journal_get_write_access(handle, bitmap_bh); + if (err) goto fail; - BUFFER_TRACE(bh2, "get_write_access"); - err = ext3_journal_get_write_access(handle, bh2); - if (err) goto fail; - gdp->bg_free_inodes_count = 0; - BUFFER_TRACE(bh2, "call ext3_journal_dirty_metadata"); - err = ext3_journal_dirty_metadata(handle, bh2); - if (err) goto fail; + if (!ext3_set_bit_atomic(sb_bgl_lock(sbi, group), + ino, bitmap_bh->b_data)) { + /* we won it */ + BUFFER_TRACE(bitmap_bh, + "call ext3_journal_dirty_metadata"); + err = ext3_journal_dirty_metadata(handle, + bitmap_bh); + if (err) + goto fail; + goto got; + } + /* we lost it */ + journal_release_buffer(handle, bitmap_bh); } - goto repeat; + + /* + * This case is possible in concurrent environment. It is very + * rare. We cannot repeat the find_group_xxx() call because + * that will simply return the same blockgroup, because the + * group descriptor metadata has not yet been updated. + * So we just go onto the next blockgroup. + */ + if (++group == sbi->s_groups_count) + group = 0; } + err = -ENOSPC; + goto out; + +got: ino += group * EXT3_INODES_PER_GROUP(sb) + 1; if (ino < EXT3_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) { ext3_error (sb, "ext3_new_inode", _