Skip to content

Commit

Permalink
ext4: cleanup transaction restarts during inode deletion
Browse files Browse the repository at this point in the history
During inode deletion, the number of journal credits that will be
needed is hard to determine.  For that reason we have journal
extend/restart calls in several places.  Whenever a transaction is
restarted, filesystem must be in a consistent state because there is
no atomicity guarantee beyond a restart call.

Add ext4_xattr_ensure_credits() helper function which takes care of
journal extend/restart logic.  It also handles getting jbd2 write
access and dirty metadata calls.  This function is called at every
iteration of handling an ea_inode reference.

Signed-off-by: Tahsin Erdogan <tahsin@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
  • Loading branch information
Tahsin Erdogan authored and tytso committed Jun 22, 2017
1 parent 02749a4 commit 30a7eb9
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 143 deletions.
66 changes: 17 additions & 49 deletions fs/ext4/inode.c
Expand Up @@ -239,7 +239,11 @@ void ext4_evict_inode(struct inode *inode)
*/
sb_start_intwrite(inode->i_sb);

handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, extra_credits);
if (!IS_NOQUOTA(inode))
extra_credits += EXT4_MAXQUOTAS_DEL_BLOCKS(inode->i_sb);

handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE,
ext4_blocks_for_truncate(inode)+extra_credits);
if (IS_ERR(handle)) {
ext4_std_error(inode->i_sb, PTR_ERR(handle));
/*
Expand All @@ -251,36 +255,9 @@ void ext4_evict_inode(struct inode *inode)
sb_end_intwrite(inode->i_sb);
goto no_delete;
}

if (IS_SYNC(inode))
ext4_handle_sync(handle);

/*
* Delete xattr inode before deleting the main inode.
*/
err = ext4_xattr_delete_inode(handle, inode, &ea_inode_array);
if (err) {
ext4_warning(inode->i_sb,
"couldn't delete inode's xattr (err %d)", err);
goto stop_handle;
}

if (!IS_NOQUOTA(inode))
extra_credits += 2 * EXT4_QUOTA_DEL_BLOCKS(inode->i_sb);

if (!ext4_handle_has_enough_credits(handle,
ext4_blocks_for_truncate(inode) + extra_credits)) {
err = ext4_journal_extend(handle,
ext4_blocks_for_truncate(inode) + extra_credits);
if (err > 0)
err = ext4_journal_restart(handle,
ext4_blocks_for_truncate(inode) + extra_credits);
if (err != 0) {
ext4_warning(inode->i_sb,
"couldn't extend journal (err %d)", err);
goto stop_handle;
}
}

inode->i_size = 0;
err = ext4_mark_inode_dirty(handle, inode);
if (err) {
Expand All @@ -298,25 +275,17 @@ void ext4_evict_inode(struct inode *inode)
}
}

/*
* ext4_ext_truncate() doesn't reserve any slop when it
* restarts journal transactions; therefore there may not be
* enough credits left in the handle to remove the inode from
* the orphan list and set the dtime field.
*/
if (!ext4_handle_has_enough_credits(handle, extra_credits)) {
err = ext4_journal_extend(handle, extra_credits);
if (err > 0)
err = ext4_journal_restart(handle, extra_credits);
if (err != 0) {
ext4_warning(inode->i_sb,
"couldn't extend journal (err %d)", err);
stop_handle:
ext4_journal_stop(handle);
ext4_orphan_del(NULL, inode);
sb_end_intwrite(inode->i_sb);
goto no_delete;
}
/* Remove xattr references. */
err = ext4_xattr_delete_inode(handle, inode, &ea_inode_array,
extra_credits);
if (err) {
ext4_warning(inode->i_sb, "xattr delete (err %d)", err);
stop_handle:
ext4_journal_stop(handle);
ext4_orphan_del(NULL, inode);
sb_end_intwrite(inode->i_sb);
ext4_xattr_inode_array_free(ea_inode_array);
goto no_delete;
}

/*
Expand All @@ -342,7 +311,6 @@ void ext4_evict_inode(struct inode *inode)
ext4_clear_inode(inode);
else
ext4_free_inode(handle, inode);

ext4_journal_stop(handle);
sb_end_intwrite(inode->i_sb);
ext4_xattr_inode_array_free(ea_inode_array);
Expand Down

0 comments on commit 30a7eb9

Please sign in to comment.