Commit acf7e244 authored by Steven Whitehouse's avatar Steven Whitehouse

GFS2: Be extra careful about deallocating inodes

There is a potential race in the inode deallocation code if two
nodes try to deallocate the same inode at the same time. Most of
the issue is solved by the iopen locking. There is still a small
window which is not covered by the iopen lock. This patches fixes
that and also makes the deallocation code more robust in the face of
any errors in the rgrp bitmaps, or erroneous iopen callbacks from
other nodes.

This does introduce one extra disk read, but that is generally not
an issue since its the same block that must be written to later
in the deallocation process. The total disk accesses therefore stay
the same,
Signed-off-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
parent 8d8291ae
...@@ -146,14 +146,11 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, ...@@ -146,14 +146,11 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
struct gfs2_inum_host *inum) struct gfs2_inum_host *inum)
{ {
struct gfs2_sbd *sdp = sb->s_fs_info; struct gfs2_sbd *sdp = sb->s_fs_info;
struct gfs2_holder i_gh, ri_gh, rgd_gh; struct gfs2_holder i_gh;
struct gfs2_rgrpd *rgd;
struct inode *inode; struct inode *inode;
struct dentry *dentry; struct dentry *dentry;
int error; int error;
/* System files? */
inode = gfs2_ilookup(sb, inum->no_addr); inode = gfs2_ilookup(sb, inum->no_addr);
if (inode) { if (inode) {
if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) { if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) {
...@@ -168,29 +165,11 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, ...@@ -168,29 +165,11 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
if (error) if (error)
return ERR_PTR(error); return ERR_PTR(error);
error = gfs2_rindex_hold(sdp, &ri_gh); error = gfs2_check_blk_type(sdp, inum->no_addr, GFS2_BLKST_DINODE);
if (error) if (error)
goto fail; goto fail;
error = -EINVAL; inode = gfs2_inode_lookup(sb, DT_UNKNOWN, inum->no_addr, 0, 0);
rgd = gfs2_blk2rgrpd(sdp, inum->no_addr);
if (!rgd)
goto fail_rindex;
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_SHARED, 0, &rgd_gh);
if (error)
goto fail_rindex;
error = -ESTALE;
if (gfs2_get_block_type(rgd, inum->no_addr) != GFS2_BLKST_DINODE)
goto fail_rgd;
gfs2_glock_dq_uninit(&rgd_gh);
gfs2_glock_dq_uninit(&ri_gh);
inode = gfs2_inode_lookup(sb, DT_UNKNOWN,
inum->no_addr,
0, 0);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
error = PTR_ERR(inode); error = PTR_ERR(inode);
goto fail; goto fail;
...@@ -224,13 +203,6 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, ...@@ -224,13 +203,6 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
if (!IS_ERR(dentry)) if (!IS_ERR(dentry))
dentry->d_op = &gfs2_dops; dentry->d_op = &gfs2_dops;
return dentry; return dentry;
fail_rgd:
gfs2_glock_dq_uninit(&rgd_gh);
fail_rindex:
gfs2_glock_dq_uninit(&ri_gh);
fail: fail:
gfs2_glock_dq_uninit(&i_gh); gfs2_glock_dq_uninit(&i_gh);
return ERR_PTR(error); return ERR_PTR(error);
......
...@@ -1256,7 +1256,7 @@ void gfs2_inplace_release(struct gfs2_inode *ip) ...@@ -1256,7 +1256,7 @@ void gfs2_inplace_release(struct gfs2_inode *ip)
* Returns: The block type (GFS2_BLKST_*) * Returns: The block type (GFS2_BLKST_*)
*/ */
unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block) static unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block)
{ {
struct gfs2_bitmap *bi = NULL; struct gfs2_bitmap *bi = NULL;
u32 length, rgrp_block, buf_block; u32 length, rgrp_block, buf_block;
...@@ -1693,6 +1693,46 @@ void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip) ...@@ -1693,6 +1693,46 @@ void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
gfs2_meta_wipe(ip, ip->i_no_addr, 1); gfs2_meta_wipe(ip, ip->i_no_addr, 1);
} }
/**
* gfs2_check_blk_type - Check the type of a block
* @sdp: The superblock
* @no_addr: The block number to check
* @type: The block type we are looking for
*
* Returns: 0 if the block type matches the expected type
* -ESTALE if it doesn't match
* or -ve errno if something went wrong while checking
*/
int gfs2_check_blk_type(struct gfs2_sbd *sdp, u64 no_addr, unsigned int type)
{
struct gfs2_rgrpd *rgd;
struct gfs2_holder ri_gh, rgd_gh;
int error;
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
goto fail;
error = -EINVAL;
rgd = gfs2_blk2rgrpd(sdp, no_addr);
if (!rgd)
goto fail_rindex;
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_SHARED, 0, &rgd_gh);
if (error)
goto fail_rindex;
if (gfs2_get_block_type(rgd, no_addr) != type)
error = -ESTALE;
gfs2_glock_dq_uninit(&rgd_gh);
fail_rindex:
gfs2_glock_dq_uninit(&ri_gh);
fail:
return error;
}
/** /**
* gfs2_rlist_add - add a RG to a list of RGs * gfs2_rlist_add - add a RG to a list of RGs
* @sdp: the filesystem * @sdp: the filesystem
......
...@@ -44,8 +44,6 @@ gfs2_inplace_reserve_i((ip), __FILE__, __LINE__) ...@@ -44,8 +44,6 @@ gfs2_inplace_reserve_i((ip), __FILE__, __LINE__)
extern void gfs2_inplace_release(struct gfs2_inode *ip); extern void gfs2_inplace_release(struct gfs2_inode *ip);
extern unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block);
extern int gfs2_alloc_block(struct gfs2_inode *ip, u64 *bn, unsigned int *n); extern int gfs2_alloc_block(struct gfs2_inode *ip, u64 *bn, unsigned int *n);
extern int gfs2_alloc_di(struct gfs2_inode *ip, u64 *bn, u64 *generation); extern int gfs2_alloc_di(struct gfs2_inode *ip, u64 *bn, u64 *generation);
...@@ -53,6 +51,8 @@ extern void gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen); ...@@ -53,6 +51,8 @@ extern void gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen);
extern void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen); extern void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen);
extern void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip); extern void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip);
extern void gfs2_unlink_di(struct inode *inode); extern void gfs2_unlink_di(struct inode *inode);
extern int gfs2_check_blk_type(struct gfs2_sbd *sdp, u64 no_addr,
unsigned int type);
struct gfs2_rgrp_list { struct gfs2_rgrp_list {
unsigned int rl_rgrps; unsigned int rl_rgrps;
......
...@@ -1286,6 +1286,10 @@ static void gfs2_delete_inode(struct inode *inode) ...@@ -1286,6 +1286,10 @@ static void gfs2_delete_inode(struct inode *inode)
goto out; goto out;
} }
error = gfs2_check_blk_type(sdp, ip->i_no_addr, GFS2_BLKST_UNLINKED);
if (error)
goto out_truncate;
gfs2_glock_dq_wait(&ip->i_iopen_gh); gfs2_glock_dq_wait(&ip->i_iopen_gh);
gfs2_holder_reinit(LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB | GL_NOCACHE, &ip->i_iopen_gh); gfs2_holder_reinit(LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB | GL_NOCACHE, &ip->i_iopen_gh);
error = gfs2_glock_nq(&ip->i_iopen_gh); error = gfs2_glock_nq(&ip->i_iopen_gh);
......
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