diff -urN ext2-7/fs/ext2/ialloc.c ext2-8/fs/ext2/ialloc.c --- ext2-7/fs/ext2/ialloc.c Fri Dec 1 11:08:56 2000 +++ ext2-8/fs/ext2/ialloc.c Fri Dec 1 11:11:12 2000 @@ -248,15 +248,111 @@ * For other inodes, search forward from the parent directory\'s block * group to find a free inode. */ + +static int find_cg_dir(struct super_block *sb, int parent_cg) +{ + struct ext2_super_block * es = sb->u.ext2_sb.s_es; + int ngroups = sb->u.ext2_sb.s_groups_count; + int avefreei = le32_to_cpu(es->s_free_inodes_count) / ngroups; + struct ext2_group_desc *cg, *best_cg = NULL; + struct buffer_head *bh, *best_bh = NULL; + int i = -1, j; + +/* I am not yet convinced that this next bit is necessary. + for (i = parent_cg, j = 0; j < ngroups; j++, i = ++i % ngroups) { + cg = ext2_get_group_desc (sb, i, &bh); + if (!cg) + continue; + if ((le16_to_cpu(cg->bg_used_dirs_count) << 8) < + le16_to_cpu(cg->bg_free_inodes_count)) { + best_cg = cg; + best_bh = bh; + goto found; + } + } +*/ + for (j = 0; j < ngroups; j++) { + cg = ext2_get_group_desc (sb, j, &bh); + if (!cg || !cg->bg_free_inodes_count) + continue; + if (le16_to_cpu(cg->bg_free_inodes_count) < avefreei) + continue; + if (!best_cg || + (le16_to_cpu(cg->bg_free_blocks_count) > + le16_to_cpu(best_cg->bg_free_blocks_count))) { + i = j; + best_cg = cg; + best_bh = bh; + } + } + if (!best_cg) + return -1; +found: + best_cg->bg_free_inodes_count = + cpu_to_le16(le16_to_cpu(best_cg->bg_free_inodes_count) - 1); + best_cg->bg_used_dirs_count = + cpu_to_le16(le16_to_cpu(best_cg->bg_used_dirs_count) + 1); + mark_buffer_dirty(best_bh); + return i; +} + +static int find_cg_other(struct super_block *sb, int parent_cg) +{ + int ngroups = sb->u.ext2_sb.s_groups_count; + struct ext2_group_desc *cg; + struct buffer_head *bh; + int i, j; + + /* + * Try to place the inode in its parent directory + */ + i = parent_cg; + cg = ext2_get_group_desc (sb, i, &bh); + if (cg && le16_to_cpu(cg->bg_free_inodes_count)) + goto found; + + /* + * Use a quadratic hash to find a group with a + * free inode + */ + for (j = 1; j < ngroups; j <<= 1) { + i += j; + if (i >= ngroups) + i -= ngroups; + cg = ext2_get_group_desc (sb, i, &bh); + if (cg && le16_to_cpu(cg->bg_free_inodes_count)) + goto found; + } + + /* + * That failed: try linear search for a free inode + */ + i = parent_cg + 1; + for (j = 2; j < ngroups; j++) { + if (++i >= ngroups) + i = 0; + cg = ext2_get_group_desc (sb, i, &bh); + if (cg && le16_to_cpu(cg->bg_free_inodes_count)) + goto found; + } + + return -1; + +found: + cg->bg_free_inodes_count = + cpu_to_le16(le16_to_cpu(cg->bg_free_inodes_count) - 1); + mark_buffer_dirty(bh); + return i; +} + struct inode * ext2_new_inode (const struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; struct buffer_head * bh2; - int i, j, avefreei; + int i, j; struct inode * inode; struct ext2_group_desc * gdp; - struct ext2_group_desc * tmp; struct ext2_super_block * es; int err; @@ -272,137 +368,41 @@ lock_super (sb); es = sb->u.ext2_sb.s_es; repeat: - gdp = NULL; i=0; - - if (S_ISDIR(mode)) { - avefreei = le32_to_cpu(es->s_free_inodes_count) / - sb->u.ext2_sb.s_groups_count; -/* I am not yet convinced that this next bit is necessary. - i = dir->u.ext2_i.i_block_group; - for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { - tmp = ext2_get_group_desc (sb, i, &bh2); - if (tmp && - (le16_to_cpu(tmp->bg_used_dirs_count) << 8) < - le16_to_cpu(tmp->bg_free_inodes_count)) { - gdp = tmp; - break; - } - else - i = ++i % sb->u.ext2_sb.s_groups_count; - } -*/ - if (!gdp) { - for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { - tmp = ext2_get_group_desc (sb, j, &bh2); - if (tmp && - le16_to_cpu(tmp->bg_free_inodes_count) && - le16_to_cpu(tmp->bg_free_inodes_count) >= avefreei) { - if (!gdp || - (le16_to_cpu(tmp->bg_free_blocks_count) > - le16_to_cpu(gdp->bg_free_blocks_count))) { - i = j; - gdp = tmp; - } - } - } - } - } + if (S_ISDIR(mode)) + i = find_cg_dir(sb, dir->u.ext2_i.i_block_group); else - { - /* - * Try to place the inode in its parent directory - */ - i = dir->u.ext2_i.i_block_group; - tmp = ext2_get_group_desc (sb, i, &bh2); - if (tmp && le16_to_cpu(tmp->bg_free_inodes_count)) - gdp = tmp; - else - { - /* - * Use a quadratic hash to find a group with a - * free inode - */ - for (j = 1; j < sb->u.ext2_sb.s_groups_count; j <<= 1) { - i += j; - if (i >= sb->u.ext2_sb.s_groups_count) - i -= sb->u.ext2_sb.s_groups_count; - tmp = ext2_get_group_desc (sb, i, &bh2); - if (tmp && - le16_to_cpu(tmp->bg_free_inodes_count)) { - gdp = tmp; - break; - } - } - } - if (!gdp) { - /* - * That failed: try linear search for a free inode - */ - i = dir->u.ext2_i.i_block_group + 1; - for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) { - if (++i >= sb->u.ext2_sb.s_groups_count) - i = 0; - tmp = ext2_get_group_desc (sb, i, &bh2); - if (tmp && - le16_to_cpu(tmp->bg_free_inodes_count)) { - gdp = tmp; - break; - } - } - } - } + i = find_cg_other(sb, dir->u.ext2_i.i_block_group); err = -ENOSPC; - if (!gdp) + if (i == -1) goto fail; err = -EIO; bh = load_inode_bitmap (sb, i); if (IS_ERR(bh)) - goto fail; - - if ((j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data, - EXT2_INODES_PER_GROUP(sb))) < - EXT2_INODES_PER_GROUP(sb)) { - if (ext2_set_bit (j, bh->b_data)) { - ext2_error (sb, "ext2_new_inode", - "bit already set for inode %d", j); - goto repeat; - } - mark_buffer_dirty(bh); - if (sb->s_flags & MS_SYNCHRONOUS) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } - } else { - if (le16_to_cpu(gdp->bg_free_inodes_count) != 0) { - ext2_error (sb, "ext2_new_inode", - "Free inodes count corrupted in group %d", - i); - /* Is it really ENOSPC? */ - err = -ENOSPC; - if (sb->s_flags & MS_RDONLY) - goto fail; + goto fail2; - gdp->bg_free_inodes_count = 0; - mark_buffer_dirty(bh2); - } - goto repeat; + j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data, + EXT2_INODES_PER_GROUP(sb)); + if (j >= EXT2_INODES_PER_GROUP(sb)) + goto bad_count; + ext2_set_bit (j, bh->b_data); + + mark_buffer_dirty(bh); + if (sb->s_flags & MS_SYNCHRONOUS) { + ll_rw_block (WRITE, 1, &bh); + wait_on_buffer (bh); } + j += i * EXT2_INODES_PER_GROUP(sb) + 1; if (j < EXT2_FIRST_INO(sb) || j > le32_to_cpu(es->s_inodes_count)) { ext2_error (sb, "ext2_new_inode", "reserved inode or inode > inodes count - " "block_group = %d,inode=%d", i, j); err = -EIO; - goto fail; + goto fail2; } - gdp->bg_free_inodes_count = - cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1); - if (S_ISDIR(mode)) - gdp->bg_used_dirs_count = - cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1); - mark_buffer_dirty(bh2); + es->s_free_inodes_count = cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1); mark_buffer_dirty(sb->u.ext2_sb.s_sbh); @@ -449,10 +449,32 @@ ext2_debug ("allocating inode %lu\n", inode->i_ino); return inode; +fail2: + gdp = ext2_get_group_desc (sb, i, &bh2); + gdp->bg_free_inodes_count = + cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1); + if (S_ISDIR(mode)) + gdp->bg_used_dirs_count = + cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1); + mark_buffer_dirty(bh2); fail: unlock_super(sb); iput(inode); return ERR_PTR(err); + +bad_count: + ext2_error (sb, "ext2_new_inode", + "Free inodes count corrupted in group %d", + i); + /* Is it really ENOSPC? */ + err = -ENOSPC; + if (sb->s_flags & MS_RDONLY) + goto fail; + + gdp = ext2_get_group_desc (sb, i, &bh2); + gdp->bg_free_inodes_count = 0; + mark_buffer_dirty(bh2); + goto repeat; } unsigned long ext2_count_free_inodes (struct super_block * sb)