ext4: move quota initialization out of inode allocation transaction
Inode allocation transaction is pretty heavy (246 credits with quotas and extents before previous patch, still around 200 after it). This is mostly due to credits required for allocation of quota structures (credits there are heavily overestimated but it's difficult to make better estimates if we don't want to wire non-trivial assumptions about quota format into filesystem). So move quota initialization out of allocation transaction. That way transaction for quota structure allocation will be started only if we need to look up quota structure on disk (rare) and furthermore it will be started for each quota type separately, not for all of them at once. This reduces maximum transaction size to 34 is most cases and to 73 in the worst case. [ Modified by tytso to clean up the cleanup paths for error handling. Also use a separate call to ext4_std_error() for each failure so it is easier for someone who is debugging a problem in this function to determine which function call failed. ] Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
This commit is contained in:
parent
fd03d8daf4
commit
eb9cc7e16b
|
@ -666,6 +666,23 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
|
||||||
ei = EXT4_I(inode);
|
ei = EXT4_I(inode);
|
||||||
sbi = EXT4_SB(sb);
|
sbi = EXT4_SB(sb);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initalize owners and quota early so that we don't have to account
|
||||||
|
* for quota initialization worst case in standard inode creating
|
||||||
|
* transaction
|
||||||
|
*/
|
||||||
|
if (owner) {
|
||||||
|
inode->i_mode = mode;
|
||||||
|
i_uid_write(inode, owner[0]);
|
||||||
|
i_gid_write(inode, owner[1]);
|
||||||
|
} else if (test_opt(sb, GRPID)) {
|
||||||
|
inode->i_mode = mode;
|
||||||
|
inode->i_uid = current_fsuid();
|
||||||
|
inode->i_gid = dir->i_gid;
|
||||||
|
} else
|
||||||
|
inode_init_owner(inode, dir, mode);
|
||||||
|
dquot_initialize(inode);
|
||||||
|
|
||||||
if (!goal)
|
if (!goal)
|
||||||
goal = sbi->s_inode_goal;
|
goal = sbi->s_inode_goal;
|
||||||
|
|
||||||
|
@ -697,7 +714,7 @@ got_group:
|
||||||
|
|
||||||
gdp = ext4_get_group_desc(sb, group, &group_desc_bh);
|
gdp = ext4_get_group_desc(sb, group, &group_desc_bh);
|
||||||
if (!gdp)
|
if (!gdp)
|
||||||
goto fail;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check free inodes count before loading bitmap.
|
* Check free inodes count before loading bitmap.
|
||||||
|
@ -711,7 +728,7 @@ got_group:
|
||||||
brelse(inode_bitmap_bh);
|
brelse(inode_bitmap_bh);
|
||||||
inode_bitmap_bh = ext4_read_inode_bitmap(sb, group);
|
inode_bitmap_bh = ext4_read_inode_bitmap(sb, group);
|
||||||
if (!inode_bitmap_bh)
|
if (!inode_bitmap_bh)
|
||||||
goto fail;
|
goto out;
|
||||||
|
|
||||||
repeat_in_this_group:
|
repeat_in_this_group:
|
||||||
ino = ext4_find_next_zero_bit((unsigned long *)
|
ino = ext4_find_next_zero_bit((unsigned long *)
|
||||||
|
@ -733,13 +750,16 @@ repeat_in_this_group:
|
||||||
handle_type, nblocks);
|
handle_type, nblocks);
|
||||||
if (IS_ERR(handle)) {
|
if (IS_ERR(handle)) {
|
||||||
err = PTR_ERR(handle);
|
err = PTR_ERR(handle);
|
||||||
goto fail;
|
ext4_std_error(sb, err);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BUFFER_TRACE(inode_bitmap_bh, "get_write_access");
|
BUFFER_TRACE(inode_bitmap_bh, "get_write_access");
|
||||||
err = ext4_journal_get_write_access(handle, inode_bitmap_bh);
|
err = ext4_journal_get_write_access(handle, inode_bitmap_bh);
|
||||||
if (err)
|
if (err) {
|
||||||
goto fail;
|
ext4_std_error(sb, err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
ext4_lock_group(sb, group);
|
ext4_lock_group(sb, group);
|
||||||
ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);
|
ret2 = ext4_test_and_set_bit(ino, inode_bitmap_bh->b_data);
|
||||||
ext4_unlock_group(sb, group);
|
ext4_unlock_group(sb, group);
|
||||||
|
@ -755,8 +775,10 @@ repeat_in_this_group:
|
||||||
got:
|
got:
|
||||||
BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata");
|
BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata");
|
||||||
err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh);
|
err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh);
|
||||||
if (err)
|
if (err) {
|
||||||
goto fail;
|
ext4_std_error(sb, err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* We may have to initialize the block bitmap if it isn't already */
|
/* We may have to initialize the block bitmap if it isn't already */
|
||||||
if (ext4_has_group_desc_csum(sb) &&
|
if (ext4_has_group_desc_csum(sb) &&
|
||||||
|
@ -768,7 +790,8 @@ got:
|
||||||
err = ext4_journal_get_write_access(handle, block_bitmap_bh);
|
err = ext4_journal_get_write_access(handle, block_bitmap_bh);
|
||||||
if (err) {
|
if (err) {
|
||||||
brelse(block_bitmap_bh);
|
brelse(block_bitmap_bh);
|
||||||
goto fail;
|
ext4_std_error(sb, err);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
BUFFER_TRACE(block_bitmap_bh, "dirty block bitmap");
|
BUFFER_TRACE(block_bitmap_bh, "dirty block bitmap");
|
||||||
|
@ -787,14 +810,18 @@ got:
|
||||||
ext4_unlock_group(sb, group);
|
ext4_unlock_group(sb, group);
|
||||||
brelse(block_bitmap_bh);
|
brelse(block_bitmap_bh);
|
||||||
|
|
||||||
if (err)
|
if (err) {
|
||||||
goto fail;
|
ext4_std_error(sb, err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BUFFER_TRACE(group_desc_bh, "get_write_access");
|
BUFFER_TRACE(group_desc_bh, "get_write_access");
|
||||||
err = ext4_journal_get_write_access(handle, group_desc_bh);
|
err = ext4_journal_get_write_access(handle, group_desc_bh);
|
||||||
if (err)
|
if (err) {
|
||||||
goto fail;
|
ext4_std_error(sb, err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* Update the relevant bg descriptor fields */
|
/* Update the relevant bg descriptor fields */
|
||||||
if (ext4_has_group_desc_csum(sb)) {
|
if (ext4_has_group_desc_csum(sb)) {
|
||||||
|
@ -840,8 +867,10 @@ got:
|
||||||
|
|
||||||
BUFFER_TRACE(group_desc_bh, "call ext4_handle_dirty_metadata");
|
BUFFER_TRACE(group_desc_bh, "call ext4_handle_dirty_metadata");
|
||||||
err = ext4_handle_dirty_metadata(handle, NULL, group_desc_bh);
|
err = ext4_handle_dirty_metadata(handle, NULL, group_desc_bh);
|
||||||
if (err)
|
if (err) {
|
||||||
goto fail;
|
ext4_std_error(sb, err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
percpu_counter_dec(&sbi->s_freeinodes_counter);
|
percpu_counter_dec(&sbi->s_freeinodes_counter);
|
||||||
if (S_ISDIR(mode))
|
if (S_ISDIR(mode))
|
||||||
|
@ -851,16 +880,6 @@ got:
|
||||||
flex_group = ext4_flex_group(sbi, group);
|
flex_group = ext4_flex_group(sbi, group);
|
||||||
atomic_dec(&sbi->s_flex_groups[flex_group].free_inodes);
|
atomic_dec(&sbi->s_flex_groups[flex_group].free_inodes);
|
||||||
}
|
}
|
||||||
if (owner) {
|
|
||||||
inode->i_mode = mode;
|
|
||||||
i_uid_write(inode, owner[0]);
|
|
||||||
i_gid_write(inode, owner[1]);
|
|
||||||
} else if (test_opt(sb, GRPID)) {
|
|
||||||
inode->i_mode = mode;
|
|
||||||
inode->i_uid = current_fsuid();
|
|
||||||
inode->i_gid = dir->i_gid;
|
|
||||||
} else
|
|
||||||
inode_init_owner(inode, dir, mode);
|
|
||||||
|
|
||||||
inode->i_ino = ino + group * EXT4_INODES_PER_GROUP(sb);
|
inode->i_ino = ino + group * EXT4_INODES_PER_GROUP(sb);
|
||||||
/* This is the optimal IO size (for stat), not the fs block size */
|
/* This is the optimal IO size (for stat), not the fs block size */
|
||||||
|
@ -889,7 +908,9 @@ got:
|
||||||
* twice.
|
* twice.
|
||||||
*/
|
*/
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto fail;
|
ext4_error(sb, "failed to insert inode %lu: doubly allocated?",
|
||||||
|
inode->i_ino);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
spin_lock(&sbi->s_next_gen_lock);
|
spin_lock(&sbi->s_next_gen_lock);
|
||||||
inode->i_generation = sbi->s_next_generation++;
|
inode->i_generation = sbi->s_next_generation++;
|
||||||
|
@ -917,7 +938,6 @@ got:
|
||||||
ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
|
ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
|
||||||
|
|
||||||
ret = inode;
|
ret = inode;
|
||||||
dquot_initialize(inode);
|
|
||||||
err = dquot_alloc_inode(inode);
|
err = dquot_alloc_inode(inode);
|
||||||
if (err)
|
if (err)
|
||||||
goto fail_drop;
|
goto fail_drop;
|
||||||
|
@ -951,24 +971,17 @@ got:
|
||||||
|
|
||||||
ext4_debug("allocating inode %lu\n", inode->i_ino);
|
ext4_debug("allocating inode %lu\n", inode->i_ino);
|
||||||
trace_ext4_allocate_inode(inode, dir, mode);
|
trace_ext4_allocate_inode(inode, dir, mode);
|
||||||
goto really_out;
|
|
||||||
fail:
|
|
||||||
ext4_std_error(sb, err);
|
|
||||||
out:
|
|
||||||
iput(inode);
|
|
||||||
ret = ERR_PTR(err);
|
|
||||||
really_out:
|
|
||||||
brelse(inode_bitmap_bh);
|
brelse(inode_bitmap_bh);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
fail_free_drop:
|
fail_free_drop:
|
||||||
dquot_free_inode(inode);
|
dquot_free_inode(inode);
|
||||||
|
|
||||||
fail_drop:
|
fail_drop:
|
||||||
dquot_drop(inode);
|
|
||||||
inode->i_flags |= S_NOQUOTA;
|
|
||||||
clear_nlink(inode);
|
clear_nlink(inode);
|
||||||
unlock_new_inode(inode);
|
unlock_new_inode(inode);
|
||||||
|
out:
|
||||||
|
dquot_drop(inode);
|
||||||
|
inode->i_flags |= S_NOQUOTA;
|
||||||
iput(inode);
|
iput(inode);
|
||||||
brelse(inode_bitmap_bh);
|
brelse(inode_bitmap_bh);
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
|
|
|
@ -2252,8 +2252,7 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
dquot_initialize(dir);
|
dquot_initialize(dir);
|
||||||
|
|
||||||
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
||||||
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
|
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
|
||||||
EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
|
|
||||||
retry:
|
retry:
|
||||||
inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0,
|
inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0,
|
||||||
NULL, EXT4_HT_DIR, credits);
|
NULL, EXT4_HT_DIR, credits);
|
||||||
|
@ -2287,8 +2286,7 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
|
||||||
dquot_initialize(dir);
|
dquot_initialize(dir);
|
||||||
|
|
||||||
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
||||||
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
|
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
|
||||||
EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
|
|
||||||
retry:
|
retry:
|
||||||
inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0,
|
inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0,
|
||||||
NULL, EXT4_HT_DIR, credits);
|
NULL, EXT4_HT_DIR, credits);
|
||||||
|
@ -2397,8 +2395,7 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
dquot_initialize(dir);
|
dquot_initialize(dir);
|
||||||
|
|
||||||
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
||||||
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
|
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3);
|
||||||
EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
|
|
||||||
retry:
|
retry:
|
||||||
inode = ext4_new_inode_start_handle(dir, S_IFDIR | mode,
|
inode = ext4_new_inode_start_handle(dir, S_IFDIR | mode,
|
||||||
&dentry->d_name,
|
&dentry->d_name,
|
||||||
|
@ -2827,8 +2824,7 @@ static int ext4_symlink(struct inode *dir,
|
||||||
* quota blocks, sb is already counted in previous macros).
|
* quota blocks, sb is already counted in previous macros).
|
||||||
*/
|
*/
|
||||||
credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
|
||||||
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
|
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3;
|
||||||
EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb);
|
|
||||||
}
|
}
|
||||||
retry:
|
retry:
|
||||||
inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO,
|
inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO,
|
||||||
|
|
Loading…
Reference in New Issue