--- /dev/null
+Index: linux-2.6.27.21-0.1/fs/ext4/ialloc.c
+===================================================================
+--- linux-2.6.27.21-0.1.orig/fs/ext4/ialloc.c
++++ linux-2.6.27.21-0.1/fs/ext4/ialloc.c
+@@ -535,12 +535,16 @@ fallback:
+ }
+
+ static int find_group_other(struct super_block *sb, struct inode *parent,
+- ext4_group_t *group)
++ ext4_group_t *group, int mode)
+ {
++ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ ext4_group_t parent_group = EXT4_I(parent)->i_block_group;
+- ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count;
++ ext4_group_t ngroups = sbi->s_groups_count;
+ struct ext4_group_desc *desc;
+ ext4_group_t i;
++ int best_group = -1;
++ ext4_fsblk_t avefreeb, freeb;
++ int best_group_freeb = 0;
+
+ /*
+ * Try to place the inode in its parent directory
+@@ -548,8 +552,10 @@ static int find_group_other(struct super
+ *group = parent_group;
+ desc = ext4_get_group_desc(sb, *group, NULL);
+ if (desc && ext4_free_inodes_count(sb, desc) &&
+- ext4_free_blks_count(sb, desc))
++ (!S_ISREG(mode) || ext4_free_blks_count(sb, desc)))
+ return 0;
++ avefreeb = ext4_free_blocks_count(sbi->s_es);
++ do_div(avefreeb, ngroups);
+
+ /*
+ * We're going to place this inode in a different blockgroup from its
+@@ -563,33 +569,49 @@ static int find_group_other(struct super
+ *group = (*group + parent->i_ino) % ngroups;
+
+ /*
+- * Use a quadratic hash to find a group with a free inode and some free
+- * blocks.
++ * Use a quadratic hash to find a group with a free inode and
++ * average number of free blocks.
+ */
+ for (i = 1; i < ngroups; i <<= 1) {
+ *group += i;
+ if (*group >= ngroups)
+ *group -= ngroups;
+ desc = ext4_get_group_desc(sb, *group, NULL);
+- if (desc && ext4_free_inodes_count(sb, desc) &&
+- ext4_free_blks_count(sb, desc))
++ if (!desc || ext4_free_inodes_count(sb, desc))
++ continue;
++ if (!S_ISREG(mode))
++ return 0;
++ if (ext4_free_blks_count(sb, desc) >= avefreeb)
+ return 0;
+ }
+
+ /*
+- * That failed: try linear search for a free inode, even if that group
+- * has no free blocks.
++ * That failed: start from last group used to allocate inode
++ * try linear search for a free inode and prefereably
++ * free blocks.
+ */
+- *group = parent_group;
++ *group = sbi->s_last_alloc_group;
++ if (*group == -1)
++ *group = parent_group;
++
+ for (i = 0; i < ngroups; i++) {
+ if (++*group >= ngroups)
+ *group = 0;
+ desc = ext4_get_group_desc(sb, *group, NULL);
+- if (desc && ext4_free_inodes_count(sb, desc))
+- return 0;
++ if (!desc || ext4_free_inodes_count(sb, desc))
++ continue;
++ freeb = ext4_free_blks_count(sb, desc);
++ if (freeb > best_group_freeb) {
++ best_group_freeb = freeb;
++ best_group = *group;
++ if (freeb >= avefreeb || !S_ISREG(mode))
++ break;
++ }
+ }
+
+- return -1;
++ sbi->s_last_alloc_group = best_group;
++ *group = best_group;
++ return 0;
+ }
+
+ /*
+@@ -755,7 +777,7 @@ continue_allocation:
+ else
+ ret2 = find_group_orlov(sb, dir, &group);
+ } else
+- ret2 = find_group_other(sb, dir, &group);
++ ret2 = find_group_other(sb, dir, &group, mode);
+
+ got_group:
+ err = -ENOSPC;
+Index: linux-2.6.27.21-0.1/fs/ext4/super.c
+===================================================================
+--- linux-2.6.27.21-0.1.orig/fs/ext4/super.c
++++ linux-2.6.27.21-0.1/fs/ext4/super.c
+@@ -2300,6 +2300,7 @@ static int ext4_fill_super(struct super_
+
+ bgl_lock_init(&sbi->s_blockgroup_lock);
+
++ sbi->s_last_alloc_group = -1;
+ for (i = 0; i < db_count; i++) {
+ block = descriptor_loc(sb, logical_sb_block, i);
+ sbi->s_group_desc[i] = sb_bread(sb, block);
+Index: linux-2.6.27.21-0.1/fs/ext4/ext4_sb.h
+===================================================================
+--- linux-2.6.27.21-0.1.orig/fs/ext4/ext4_sb.h
++++ linux-2.6.27.21-0.1/fs/ext4/ext4_sb.h
+@@ -64,6 +64,8 @@ struct ext4_sb_info {
+ struct percpu_counter s_dirtyblocks_counter;
+ struct blockgroup_lock s_blockgroup_lock;
+ struct proc_dir_entry *s_proc;
++ /* Last group used to allocate inode */
++ ext4_group_t s_last_alloc_group;
+
+ /* root of the per fs reservation window tree */
+ spinlock_t s_rsv_window_lock;