Index: linux-2.6.7/fs/ext3/ialloc.c =================================================================== --- linux-2.6.7.orig/fs/ext3/ialloc.c 2004-10-05 21:15:58.000000000 +0400 +++ linux-2.6.7/fs/ext3/ialloc.c 2004-10-06 17:14:39.776562240 +0400 @@ -144,11 +144,16 @@ if (!bitmap_bh) goto error_return; - BUFFER_TRACE(bitmap_bh, "get_write_access"); - fatal = ext3_journal_get_write_access(handle, bitmap_bh); + BUFFER_TRACE(bitmap_bh, "get_undo_access"); + fatal = ext3_journal_get_undo_access(handle, bitmap_bh, NULL); if (fatal) goto error_return; + /* to prevent inode reusing within single transaction -bzzz */ + BUFFER_TRACE(bitmap_bh, "clear in b_committed_data"); + J_ASSERT_BH(bitmap_bh, bh2jh(bitmap_bh)->b_committed_data != NULL); + ext3_set_bit(bit, bh2jh(bitmap_bh)->b_committed_data); + /* Ok, now we can actually update the inode bitmaps.. */ if (!ext3_clear_bit_atomic(sb_bgl_lock(sbi, block_group), bit, bitmap_bh->b_data)) @@ -411,6 +416,16 @@ } /* + * this is just to check was block freed in current transaction -bzzz + */ +static int ext3_test_allocatable(int nr, struct buffer_head *bh) +{ + if (!buffer_jbd(bh) || !bh2jh(bh)->b_committed_data) + return 1; + return !ext3_test_bit(nr, bh2jh(bh)->b_committed_data); +} + +/* * There are two policies for allocating an inode. If the new inode is * a directory, then a forward search is made for a block group with both * free space and a low directory-to-inode ratio; if that fails, then of @@ -426,7 +441,7 @@ struct super_block *sb; struct buffer_head *bitmap_bh = NULL; struct buffer_head *bh2; - int group; + int group, credits; unsigned long ino = 0; struct inode * inode; struct ext3_group_desc * gdp = NULL; @@ -459,16 +474,25 @@ if (!bitmap_bh) goto fail; - BUFFER_TRACE(bh, "get_write_access"); - err = ext3_journal_get_write_access(handle, bitmap_bh); + BUFFER_TRACE(bh, "get_undo_access"); + err = ext3_journal_get_undo_access(handle, bitmap_bh, &credits); if (err) goto fail; - if (ext3_set_bit_atomic(sb_bgl_lock(sbi, group), - ino, bitmap_bh->b_data)) { + if (!ext3_test_allocatable(ino, bitmap_bh) || + ext3_set_bit_atomic(sb_bgl_lock(sbi, group), + ino, bitmap_bh->b_data)) { printk(KERN_ERR "goal inode %lu unavailable\n", goal); /* Oh well, we tried. */ goto continue_allocation; } + if (!ext3_test_allocatable(ino, bitmap_bh)) { + /* in theory it's possible another that thread has + * allocated and freed block between test_allocatable() + * and set_bit_atomic(). so, we we have to check + * this -bzzz */ + journal_release_buffer(handle, bitmap_bh, credits); + goto continue_allocation; + } BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); err = ext3_journal_dirty_metadata(handle, bitmap_bh); @@ -511,21 +535,29 @@ int credits = 0; BUFFER_TRACE(bitmap_bh, "get_write_access"); - err = ext3_journal_get_write_access_credits(handle, + err = ext3_journal_get_undo_access(handle, bitmap_bh, &credits); 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; + if (ext3_test_allocatable(ino, bitmap_bh) && + !ext3_set_bit_atomic(sb_bgl_lock(sbi, group), + ino, bitmap_bh->b_data)) { + /* in theory it's possible another that + * thread has allocated and freed block + * between ext3_test_allocatable() and + * set_bit_atomic(). so, we we have to + * check this -bzzz */ + if (ext3_test_allocatable(ino, bitmap_bh)) { + /* 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, credits);