diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 08c6dd0c6713..9949bb746a52 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1502,7 +1502,7 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name) inode = gfs2_inode_lookup(dir->i_sb, be16_to_cpu(dent->de_type), be64_to_cpu(dent->de_inum.no_addr), - be64_to_cpu(dent->de_inum.no_formal_ino)); + be64_to_cpu(dent->de_inum.no_formal_ino), 0); brelse(bh); return inode; } diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 013f00b90cc2..5f6dc32946cd 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -77,6 +77,49 @@ static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr) return iget5_locked(sb, hash, iget_test, iget_set, &no_addr); } +struct gfs2_skip_data { + u64 no_addr; + int skipped; +}; + +static int iget_skip_test(struct inode *inode, void *opaque) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_skip_data *data = opaque; + + if (ip->i_no_addr == data->no_addr && inode->i_private != NULL){ + if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)){ + data->skipped = 1; + return 0; + } + return 1; + } + return 0; +} + +static int iget_skip_set(struct inode *inode, void *opaque) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_skip_data *data = opaque; + + if (data->skipped) + return 1; + inode->i_ino = (unsigned long)(data->no_addr); + ip->i_no_addr = data->no_addr; + return 0; +} + +static struct inode *gfs2_iget_skip(struct super_block *sb, + u64 no_addr) +{ + struct gfs2_skip_data data; + unsigned long hash = (unsigned long)no_addr; + + data.no_addr = no_addr; + data.skipped = 0; + return iget5_locked(sb, hash, iget_skip_test, iget_skip_set, &data); +} + /** * GFS2 lookup code fills in vfs inode contents based on info obtained * from directory entry inside gfs2_inode_lookup(). This has caused issues @@ -112,6 +155,7 @@ void gfs2_set_iop(struct inode *inode) * @sb: The super block * @no_addr: The inode number * @type: The type of the inode + * @skip_freeing: set this not return an inode if it is currently being freed. * * Returns: A VFS inode, or an error */ @@ -119,13 +163,19 @@ void gfs2_set_iop(struct inode *inode) struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, u64 no_addr, - u64 no_formal_ino) + u64 no_formal_ino, int skip_freeing) { - struct inode *inode = gfs2_iget(sb, no_addr); - struct gfs2_inode *ip = GFS2_I(inode); + struct inode *inode; + struct gfs2_inode *ip; struct gfs2_glock *io_gl; int error; + if (skip_freeing) + inode = gfs2_iget_skip(sb, no_addr); + else + inode = gfs2_iget(sb, no_addr); + ip = GFS2_I(inode); + if (!inode) return ERR_PTR(-ENOBUFS); @@ -949,7 +999,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, - inum.no_formal_ino); + inum.no_formal_ino, 0); if (IS_ERR(inode)) goto fail_gunlock2; diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index 4517ac82c01c..351ac87ab384 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -49,7 +49,8 @@ static inline void gfs2_inum_out(const struct gfs2_inode *ip, void gfs2_inode_attr_in(struct gfs2_inode *ip); void gfs2_set_iop(struct inode *inode); struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, - u64 no_addr, u64 no_formal_ino); + u64 no_addr, u64 no_formal_ino, + int skip_freeing); struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr); int gfs2_inode_refresh(struct gfs2_inode *ip); diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c index b8312edee0e4..e2d1347796a9 100644 --- a/fs/gfs2/ops_export.c +++ b/fs/gfs2/ops_export.c @@ -237,7 +237,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj) inode = gfs2_inode_lookup(sb, DT_UNKNOWN, inum->no_addr, - 0); + 0, 0); if (!inode) goto fail; if (IS_ERR(inode)) { diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 1ac9afa58c65..17de58e83d92 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -230,7 +230,7 @@ fail: static inline struct inode *gfs2_lookup_root(struct super_block *sb, u64 no_addr) { - return gfs2_inode_lookup(sb, DT_DIR, no_addr, 0); + return gfs2_inode_lookup(sb, DT_DIR, no_addr, 0, 0); } static int init_sb(struct gfs2_sbd *sdp, int silent, int undo) diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 2d7f7ea0c9a8..708c287e1d0e 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -884,7 +884,7 @@ static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked) continue; *last_unlinked = no_addr; inode = gfs2_inode_lookup(rgd->rd_sbd->sd_vfs, DT_UNKNOWN, - no_addr, -1); + no_addr, -1, 1); if (!IS_ERR(inode)) return inode; }