diff options
author | Bob Peterson <rpeterso@redhat.com> | 2010-04-14 11:58:16 -0400 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2010-04-14 16:48:05 +0100 |
commit | 1a0eae8848cde6e0734360f6456496c995ee1e23 (patch) | |
tree | 536f944468ef5bc6f47ec09325422adbc5a3907a /fs/gfs2/inode.c | |
parent | 602c89d2e3e8652f94a697c9a919be739b9bcdd5 (diff) | |
download | linux-3.10-1a0eae8848cde6e0734360f6456496c995ee1e23.tar.gz linux-3.10-1a0eae8848cde6e0734360f6456496c995ee1e23.tar.bz2 linux-3.10-1a0eae8848cde6e0734360f6456496c995ee1e23.zip |
GFS2: glock livelock
This patch fixes a couple gfs2 problems with the reclaiming of
unlinked dinodes. First, there were a couple of livelocks where
everything would come to a halt waiting for a glock that was
seemingly held by a process that no longer existed. In fact, the
process did exist, it just had the wrong pid number in the holder
information. Second, there was a lock ordering problem between
inode locking and glock locking. Third, glock/inode contention
could sometimes cause inodes to be improperly marked invalid by
iget_failed.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Diffstat (limited to 'fs/gfs2/inode.c')
-rw-r--r-- | fs/gfs2/inode.c | 101 |
1 files changed, 92 insertions, 9 deletions
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index b1bf2694fb2..51d8061fa07 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -158,7 +158,6 @@ 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 */ @@ -166,17 +165,14 @@ 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, int skip_freeing) + u64 no_formal_ino) { 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); + inode = gfs2_iget(sb, no_addr); ip = GFS2_I(inode); if (!inode) @@ -234,13 +230,100 @@ fail_glock: fail_iopen: gfs2_glock_put(io_gl); fail_put: - ip->i_gl->gl_object = NULL; + if (inode->i_state & I_NEW) + ip->i_gl->gl_object = NULL; gfs2_glock_put(ip->i_gl); fail: - iget_failed(inode); + if (inode->i_state & I_NEW) + iget_failed(inode); + else + iput(inode); return ERR_PTR(error); } +/** + * gfs2_unlinked_inode_lookup - Lookup an unlinked inode for reclamation + * @sb: The super block + * no_addr: The inode number + * @@inode: A pointer to the inode found, if any + * + * Returns: 0 and *inode if no errors occurred. If an error occurs, + * the resulting *inode may or may not be NULL. + */ + +int gfs2_unlinked_inode_lookup(struct super_block *sb, u64 no_addr, + struct inode **inode) +{ + struct gfs2_sbd *sdp; + struct gfs2_inode *ip; + struct gfs2_glock *io_gl; + int error; + struct gfs2_holder gh; + + *inode = gfs2_iget_skip(sb, no_addr); + + if (!(*inode)) + return -ENOBUFS; + + if (!((*inode)->i_state & I_NEW)) + return -ENOBUFS; + + ip = GFS2_I(*inode); + sdp = GFS2_SB(*inode); + ip->i_no_formal_ino = -1; + + error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); + if (unlikely(error)) + goto fail; + ip->i_gl->gl_object = ip; + + error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl); + if (unlikely(error)) + goto fail_put; + + set_bit(GIF_INVALID, &ip->i_flags); + error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, LM_FLAG_TRY | GL_EXACT, + &ip->i_iopen_gh); + if (unlikely(error)) { + if (error == GLR_TRYFAILED) + error = 0; + goto fail_iopen; + } + ip->i_iopen_gh.gh_gl->gl_object = ip; + gfs2_glock_put(io_gl); + + (*inode)->i_mode = DT2IF(DT_UNKNOWN); + + /* + * We must read the inode in order to work out its type in + * this case. Note that this doesn't happen often as we normally + * know the type beforehand. This code path only occurs during + * unlinked inode recovery (where it is safe to do this glock, + * which is not true in the general case). + */ + error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, LM_FLAG_TRY, + &gh); + if (unlikely(error)) { + if (error == GLR_TRYFAILED) + error = 0; + goto fail_glock; + } + /* Inode is now uptodate */ + gfs2_glock_dq_uninit(&gh); + gfs2_set_iop(*inode); + + return 0; +fail_glock: + gfs2_glock_dq(&ip->i_iopen_gh); +fail_iopen: + gfs2_glock_put(io_gl); +fail_put: + ip->i_gl->gl_object = NULL; + gfs2_glock_put(ip->i_gl); +fail: + return error; +} + static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) { const struct gfs2_dinode *str = buf; @@ -862,7 +945,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, goto fail_gunlock2; inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, - inum.no_formal_ino, 0); + inum.no_formal_ino); if (IS_ERR(inode)) goto fail_gunlock2; |