gfs2: deallocate inodes in gfs2_create_inode
authorAndreas Gruenbacher <agruenba@redhat.com>
Fri, 18 Apr 2025 14:41:52 +0000 (16:41 +0200)
committerAndreas Gruenbacher <agruenba@redhat.com>
Thu, 24 Apr 2025 21:10:05 +0000 (23:10 +0200)
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: 9ffa18884cce ("gfs2: gl_object races fix")
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
fs/gfs2/inode.c
fs/gfs2/super.c

index 0b2d75106b1cfdfda41528cf4a196fd3e02e917e..187d789a8f1e3a270c8f4b7526f18f6061769315 100644 (file)
@@ -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;
 
@@ -842,8 +843,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);
 
@@ -898,6 +901,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);
@@ -912,10 +927,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
index e25a24ae2197fd28c3052b1ce9eca36eba7d4fa1..54f1efd47a3e5fe213d251998124d7a5917d4c65 100644 (file)
@@ -1258,9 +1258,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;
@@ -1290,7 +1287,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))
                return gfs2_upgrade_iopen_glock(inode);
@@ -1314,7 +1310,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;
        }