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;