Commit 9b88f9fb authored by Jan Kara's avatar Jan Kara Committed by Theodore Ts'o

ext4: Do not iput inode under running transaction

When ext4_mkdir(), ext4_symlink(), ext4_create(), or ext4_mknod() fail
to add entry into directory, it ends up dropping freshly created inode
under the running transaction and thus inode truncation happens under
that transaction. That breaks assumptions that evict() does not get
called from a transaction context and at least in ext4_symlink() case it
can result in inode eviction deadlocking in inode_wait_for_writeback()
when flush worker finds symlink inode, starts to write it back and
blocks on starting a transaction. So change the code in ext4_mkdir() and
ext4_add_nondir() to drop inode reference only after the transaction is
stopped. We also have to add inode to the orphan list in that case as
otherwise the inode would get leaked in case we crash before inode
deletion is committed.

CC: stable@vger.kernel.org
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20191105164437.32602-5-jack@suse.czSigned-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent a9e26328
...@@ -2547,21 +2547,29 @@ static void ext4_dec_count(handle_t *handle, struct inode *inode) ...@@ -2547,21 +2547,29 @@ static void ext4_dec_count(handle_t *handle, struct inode *inode)
} }
/*
* Add non-directory inode to a directory. On success, the inode reference is
* consumed by dentry is instantiation. This is also indicated by clearing of
* *inodep pointer. On failure, the caller is responsible for dropping the
* inode reference in the safe context.
*/
static int ext4_add_nondir(handle_t *handle, static int ext4_add_nondir(handle_t *handle,
struct dentry *dentry, struct inode *inode) struct dentry *dentry, struct inode **inodep)
{ {
struct inode *dir = d_inode(dentry->d_parent); struct inode *dir = d_inode(dentry->d_parent);
struct inode *inode = *inodep;
int err = ext4_add_entry(handle, dentry, inode); int err = ext4_add_entry(handle, dentry, inode);
if (!err) { if (!err) {
ext4_mark_inode_dirty(handle, inode); ext4_mark_inode_dirty(handle, inode);
if (IS_DIRSYNC(dir)) if (IS_DIRSYNC(dir))
ext4_handle_sync(handle); ext4_handle_sync(handle);
d_instantiate_new(dentry, inode); d_instantiate_new(dentry, inode);
*inodep = NULL;
return 0; return 0;
} }
drop_nlink(inode); drop_nlink(inode);
ext4_orphan_add(handle, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
iput(inode);
return err; return err;
} }
...@@ -2595,10 +2603,12 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -2595,10 +2603,12 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode,
inode->i_op = &ext4_file_inode_operations; inode->i_op = &ext4_file_inode_operations;
inode->i_fop = &ext4_file_operations; inode->i_fop = &ext4_file_operations;
ext4_set_aops(inode); ext4_set_aops(inode);
err = ext4_add_nondir(handle, dentry, inode); err = ext4_add_nondir(handle, dentry, &inode);
} }
if (handle) if (handle)
ext4_journal_stop(handle); ext4_journal_stop(handle);
if (!IS_ERR_OR_NULL(inode))
iput(inode);
if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
goto retry; goto retry;
return err; return err;
...@@ -2625,10 +2635,12 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry, ...@@ -2625,10 +2635,12 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
if (!IS_ERR(inode)) { if (!IS_ERR(inode)) {
init_special_inode(inode, inode->i_mode, rdev); init_special_inode(inode, inode->i_mode, rdev);
inode->i_op = &ext4_special_inode_operations; inode->i_op = &ext4_special_inode_operations;
err = ext4_add_nondir(handle, dentry, inode); err = ext4_add_nondir(handle, dentry, &inode);
} }
if (handle) if (handle)
ext4_journal_stop(handle); ext4_journal_stop(handle);
if (!IS_ERR_OR_NULL(inode))
iput(inode);
if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
goto retry; goto retry;
return err; return err;
...@@ -2778,10 +2790,12 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -2778,10 +2790,12 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
if (err) { if (err) {
out_clear_inode: out_clear_inode:
clear_nlink(inode); clear_nlink(inode);
ext4_orphan_add(handle, inode);
unlock_new_inode(inode); unlock_new_inode(inode);
ext4_mark_inode_dirty(handle, inode); ext4_mark_inode_dirty(handle, inode);
ext4_journal_stop(handle);
iput(inode); iput(inode);
goto out_stop; goto out_retry;
} }
ext4_inc_count(handle, dir); ext4_inc_count(handle, dir);
ext4_update_dx_flag(dir); ext4_update_dx_flag(dir);
...@@ -2795,6 +2809,7 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -2795,6 +2809,7 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
out_stop: out_stop:
if (handle) if (handle)
ext4_journal_stop(handle); ext4_journal_stop(handle);
out_retry:
if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
goto retry; goto retry;
return err; return err;
...@@ -3327,9 +3342,11 @@ static int ext4_symlink(struct inode *dir, ...@@ -3327,9 +3342,11 @@ static int ext4_symlink(struct inode *dir,
inode->i_size = disk_link.len - 1; inode->i_size = disk_link.len - 1;
} }
EXT4_I(inode)->i_disksize = inode->i_size; EXT4_I(inode)->i_disksize = inode->i_size;
err = ext4_add_nondir(handle, dentry, inode); err = ext4_add_nondir(handle, dentry, &inode);
if (handle) if (handle)
ext4_journal_stop(handle); ext4_journal_stop(handle);
if (inode)
iput(inode);
goto out_free_encrypted_link; goto out_free_encrypted_link;
err_drop_inode: err_drop_inode:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment