gfs2: deallocate inodes in gfs2_create_inode
[ Upstream commit 2c63986dd35fa9eb0d7d1530b5eb2244b7296e22 ]
When creating and destroying inodes, we are relying on the inode hash
table to make sure that for a given inode number, only a single inode
will exist. We then link that inode to its inode and iopen glock and
let those glocks point back at the inode. However, when iget_failed()
is called, the inode is removed from the inode hash table before
gfs_evict_inode() is called, and uniqueness is no longer guaranteed.
Commit f1046a472b70 ("gfs2: gl_object races fix") was trying to work
around that problem by detaching the inode glock from the inode before
calling iget_failed(), but that broke the inode deallocation code in
gfs_evict_inode().
To fix that, deallocate partially created inodes in gfs2_create_inode()
instead of relying on gfs_evict_inode() for doing that.
This means that gfs2_evict_inode() and its helper functions will no
longer see partially created inodes, and so some simplifications are
possible there.
Fixes: 9ffa18884c ("gfs2: gl_object races fix")
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
8e753fc3d5
commit
cde7f94078
@@ -697,10 +697,11 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
||||
struct gfs2_inode *dip = GFS2_I(dir), *ip;
|
||||
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
|
||||
struct gfs2_glock *io_gl;
|
||||
int error;
|
||||
int error, dealloc_error;
|
||||
u32 aflags = 0;
|
||||
unsigned blocks = 1;
|
||||
struct gfs2_diradd da = { .bh = NULL, .save_loc = 1, };
|
||||
bool xattr_initialized = false;
|
||||
|
||||
if (!name->len || name->len > GFS2_FNAMESIZE)
|
||||
return -ENAMETOOLONG;
|
||||
@@ -813,11 +814,11 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl);
|
||||
if (error)
|
||||
goto fail_free_inode;
|
||||
goto fail_dealloc_inode;
|
||||
|
||||
error = gfs2_glock_get(sdp, ip->i_no_addr, &gfs2_iopen_glops, CREATE, &io_gl);
|
||||
if (error)
|
||||
goto fail_free_inode;
|
||||
goto fail_dealloc_inode;
|
||||
gfs2_cancel_delete_work(io_gl);
|
||||
io_gl->gl_no_formal_ino = ip->i_no_formal_ino;
|
||||
|
||||
@@ -841,8 +842,10 @@ retry:
|
||||
if (error)
|
||||
goto fail_gunlock3;
|
||||
|
||||
if (blocks > 1)
|
||||
if (blocks > 1) {
|
||||
gfs2_init_xattr(ip);
|
||||
xattr_initialized = true;
|
||||
}
|
||||
init_dinode(dip, ip, symname);
|
||||
gfs2_trans_end(sdp);
|
||||
|
||||
@@ -897,6 +900,18 @@ fail_gunlock3:
|
||||
gfs2_glock_dq_uninit(&ip->i_iopen_gh);
|
||||
fail_gunlock2:
|
||||
gfs2_glock_put(io_gl);
|
||||
fail_dealloc_inode:
|
||||
set_bit(GIF_ALLOC_FAILED, &ip->i_flags);
|
||||
dealloc_error = 0;
|
||||
if (ip->i_eattr)
|
||||
dealloc_error = gfs2_ea_dealloc(ip, xattr_initialized);
|
||||
clear_nlink(inode);
|
||||
mark_inode_dirty(inode);
|
||||
if (!dealloc_error)
|
||||
dealloc_error = gfs2_dinode_dealloc(ip);
|
||||
if (dealloc_error)
|
||||
fs_warn(sdp, "%s: %d\n", __func__, dealloc_error);
|
||||
ip->i_no_addr = 0;
|
||||
fail_free_inode:
|
||||
if (ip->i_gl) {
|
||||
gfs2_glock_put(ip->i_gl);
|
||||
@@ -911,10 +926,6 @@ fail_gunlock:
|
||||
gfs2_dir_no_add(&da);
|
||||
gfs2_glock_dq_uninit(&d_gh);
|
||||
if (!IS_ERR_OR_NULL(inode)) {
|
||||
set_bit(GIF_ALLOC_FAILED, &ip->i_flags);
|
||||
clear_nlink(inode);
|
||||
if (ip->i_no_addr)
|
||||
mark_inode_dirty(inode);
|
||||
if (inode->i_state & I_NEW)
|
||||
iget_failed(inode);
|
||||
else
|
||||
|
||||
@@ -1255,9 +1255,6 @@ static enum evict_behavior evict_should_delete(struct inode *inode,
|
||||
struct gfs2_sbd *sdp = sb->s_fs_info;
|
||||
int ret;
|
||||
|
||||
if (unlikely(test_bit(GIF_ALLOC_FAILED, &ip->i_flags)))
|
||||
goto should_delete;
|
||||
|
||||
if (gfs2_holder_initialized(&ip->i_iopen_gh) &&
|
||||
test_bit(GLF_DEFER_DELETE, &ip->i_iopen_gh.gh_gl->gl_flags))
|
||||
return EVICT_SHOULD_DEFER_DELETE;
|
||||
@@ -1291,7 +1288,6 @@ static enum evict_behavior evict_should_delete(struct inode *inode,
|
||||
if (inode->i_nlink)
|
||||
return EVICT_SHOULD_SKIP_DELETE;
|
||||
|
||||
should_delete:
|
||||
if (gfs2_holder_initialized(&ip->i_iopen_gh) &&
|
||||
test_bit(HIF_HOLDER, &ip->i_iopen_gh.gh_iflags)) {
|
||||
if (!gfs2_upgrade_iopen_glock(inode)) {
|
||||
@@ -1319,7 +1315,7 @@ static int evict_unlinked_inode(struct inode *inode)
|
||||
}
|
||||
|
||||
if (ip->i_eattr) {
|
||||
ret = gfs2_ea_dealloc(ip, !test_bit(GIF_ALLOC_FAILED, &ip->i_flags));
|
||||
ret = gfs2_ea_dealloc(ip, true);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user