GFS2: stuck in inode wait, no glocks stuck

This patch changes the lock ordering when gfs2 reclaims
unlinked dinodes, thereby avoiding a livelock.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
This commit is contained in:
Bob Peterson 2010-05-11 17:58:11 -04:00 committed by Steven Whitehouse
parent eaefbf968a
commit cc0581bd61

View File

@ -952,16 +952,14 @@ static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_alloc *al)
* The inode, if one has been found, in inode. * The inode, if one has been found, in inode.
*/ */
static int try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, static u64 try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked,
u64 skip, struct inode **inode) u64 skip)
{ {
u32 goal = 0, block; u32 goal = 0, block;
u64 no_addr; u64 no_addr;
struct gfs2_sbd *sdp = rgd->rd_sbd; struct gfs2_sbd *sdp = rgd->rd_sbd;
unsigned int n; unsigned int n;
int error = 0;
*inode = NULL;
for(;;) { for(;;) {
if (goal >= rgd->rd_data) if (goal >= rgd->rd_data)
break; break;
@ -981,10 +979,7 @@ static int try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked,
if (no_addr == skip) if (no_addr == skip)
continue; continue;
*last_unlinked = no_addr; *last_unlinked = no_addr;
error = gfs2_unlinked_inode_lookup(rgd->rd_sbd->sd_vfs, return no_addr;
no_addr, inode);
if (*inode || error)
return error;
} }
rgd->rd_flags &= ~GFS2_RDF_CHECK; rgd->rd_flags &= ~GFS2_RDF_CHECK;
@ -1069,11 +1064,12 @@ static void forward_rgrp_set(struct gfs2_sbd *sdp, struct gfs2_rgrpd *rgd)
* Try to acquire rgrp in way which avoids contending with others. * Try to acquire rgrp in way which avoids contending with others.
* *
* Returns: errno * Returns: errno
* unlinked: the block address of an unlinked block to be reclaimed
*/ */
static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) static int get_local_rgrp(struct gfs2_inode *ip, u64 *unlinked,
u64 *last_unlinked)
{ {
struct inode *inode = NULL;
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_rgrpd *rgd, *begin = NULL; struct gfs2_rgrpd *rgd, *begin = NULL;
struct gfs2_alloc *al = ip->i_alloc; struct gfs2_alloc *al = ip->i_alloc;
@ -1082,6 +1078,7 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
int loops = 0; int loops = 0;
int error, rg_locked; int error, rg_locked;
*unlinked = 0;
rgd = gfs2_blk2rgrpd(sdp, ip->i_goal); rgd = gfs2_blk2rgrpd(sdp, ip->i_goal);
while (rgd) { while (rgd) {
@ -1103,29 +1100,19 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
because that would require an iput which can only because that would require an iput which can only
happen after the rgrp is unlocked. */ happen after the rgrp is unlocked. */
if (!rg_locked && rgd->rd_flags & GFS2_RDF_CHECK) if (!rg_locked && rgd->rd_flags & GFS2_RDF_CHECK)
error = try_rgrp_unlink(rgd, last_unlinked, *unlinked = try_rgrp_unlink(rgd, last_unlinked,
ip->i_no_addr, &inode); ip->i_no_addr);
if (!rg_locked) if (!rg_locked)
gfs2_glock_dq_uninit(&al->al_rgd_gh); gfs2_glock_dq_uninit(&al->al_rgd_gh);
if (inode) { if (*unlinked)
if (error) { return -EAGAIN;
if (inode->i_state & I_NEW)
iget_failed(inode);
else
iput(inode);
return ERR_PTR(error);
}
return inode;
}
if (error)
return ERR_PTR(error);
/* fall through */ /* fall through */
case GLR_TRYFAILED: case GLR_TRYFAILED:
rgd = recent_rgrp_next(rgd); rgd = recent_rgrp_next(rgd);
break; break;
default: default:
return ERR_PTR(error); return error;
} }
} }
@ -1148,22 +1135,12 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
if (try_rgrp_fit(rgd, al)) if (try_rgrp_fit(rgd, al))
goto out; goto out;
if (!rg_locked && rgd->rd_flags & GFS2_RDF_CHECK) if (!rg_locked && rgd->rd_flags & GFS2_RDF_CHECK)
error = try_rgrp_unlink(rgd, last_unlinked, *unlinked = try_rgrp_unlink(rgd, last_unlinked,
ip->i_no_addr, &inode); ip->i_no_addr);
if (!rg_locked) if (!rg_locked)
gfs2_glock_dq_uninit(&al->al_rgd_gh); gfs2_glock_dq_uninit(&al->al_rgd_gh);
if (inode) { if (*unlinked)
if (error) { return -EAGAIN;
if (inode->i_state & I_NEW)
iget_failed(inode);
else
iput(inode);
return ERR_PTR(error);
}
return inode;
}
if (error)
return ERR_PTR(error);
break; break;
case GLR_TRYFAILED: case GLR_TRYFAILED:
@ -1171,7 +1148,7 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
break; break;
default: default:
return ERR_PTR(error); return error;
} }
rgd = gfs2_rgrpd_get_next(rgd); rgd = gfs2_rgrpd_get_next(rgd);
@ -1180,7 +1157,7 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
if (rgd == begin) { if (rgd == begin) {
if (++loops >= 3) if (++loops >= 3)
return ERR_PTR(-ENOSPC); return -ENOSPC;
if (!skipped) if (!skipped)
loops++; loops++;
flags = 0; flags = 0;
@ -1200,7 +1177,7 @@ out:
forward_rgrp_set(sdp, rgd); forward_rgrp_set(sdp, rgd);
} }
return NULL; return 0;
} }
/** /**
@ -1216,7 +1193,7 @@ int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, unsigned int line)
struct gfs2_alloc *al = ip->i_alloc; struct gfs2_alloc *al = ip->i_alloc;
struct inode *inode; struct inode *inode;
int error = 0; int error = 0;
u64 last_unlinked = NO_BLOCK; u64 last_unlinked = NO_BLOCK, unlinked;
if (gfs2_assert_warn(sdp, al->al_requested)) if (gfs2_assert_warn(sdp, al->al_requested))
return -EINVAL; return -EINVAL;
@ -1232,14 +1209,19 @@ try_again:
if (error) if (error)
return error; return error;
inode = get_local_rgrp(ip, &last_unlinked); error = get_local_rgrp(ip, &unlinked, &last_unlinked);
if (inode) { if (error) {
if (ip != GFS2_I(sdp->sd_rindex)) if (ip != GFS2_I(sdp->sd_rindex))
gfs2_glock_dq_uninit(&al->al_ri_gh); gfs2_glock_dq_uninit(&al->al_ri_gh);
if (IS_ERR(inode)) if (error != -EAGAIN)
return PTR_ERR(inode); return error;
iput(inode); error = gfs2_unlinked_inode_lookup(ip->i_inode.i_sb,
unlinked, &inode);
if (inode)
iput(inode);
gfs2_log_flush(sdp, NULL); gfs2_log_flush(sdp, NULL);
if (error == GLR_TRYFAILED)
error = 0;
goto try_again; goto try_again;
} }