Index: linux-2.6.18.i386/fs/ext4/ialloc.c =================================================================== --- linux-2.6.18.i386.orig/fs/ext4/ialloc.c +++ linux-2.6.18.i386/fs/ext4/ialloc.c @@ -509,12 +509,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 @@ -522,8 +526,10 @@ static int find_group_other(struct super *group = parent_group; desc = ext4_get_group_desc(sb, *group, NULL); if (desc && le16_to_cpu(desc->bg_free_inodes_count) && - le16_to_cpu(desc->bg_free_blocks_count)) + (!S_ISREG(mode) || le16_to_cpu(desc->bg_free_blocks_count))) 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 @@ -537,33 +543,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 && le16_to_cpu(desc->bg_free_inodes_count) && - le16_to_cpu(desc->bg_free_blocks_count)) + if (!desc || !desc->bg_free_inodes_count) + continue; + if (!S_ISREG(mode)) + return 0; + if (le16_to_cpu(desc->bg_free_blocks_count) >= 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 && le16_to_cpu(desc->bg_free_inodes_count)) - return 0; + if (!desc || !desc->bg_free_inodes_count) + continue; + freeb = le16_to_cpu(desc->bg_free_blocks_count); + 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; } /* @@ -656,7 +678,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.18.i386/fs/ext4/super.c =================================================================== --- linux-2.6.18.i386.orig/fs/ext4/super.c +++ linux-2.6.18.i386/fs/ext4/super.c @@ -2190,6 +2190,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.18.i386/fs/ext4/ext4_sb.h =================================================================== --- linux-2.6.18.i386.orig/fs/ext4/ext4_sb.h +++ linux-2.6.18.i386/fs/ext4/ext4_sb.h @@ -60,6 +60,8 @@ struct ext4_sb_info { struct percpu_counter s_freeinodes_counter; struct percpu_counter s_dirs_counter; struct blockgroup_lock s_blockgroup_lock; + /* Last group used to allocate inode */ + int s_last_alloc_group; /* root of the per fs reservation window tree */ spinlock_t s_rsv_window_lock;