Commit 292c8c14 authored by Abhijith Das's avatar Abhijith Das Committed by Steven Whitehouse

[GFS2] patch to check for recursive lock requests in gfs2_rename code path

A certain scenario in the rename code path triggers a kernel BUG()
because it accidentally does recursive locking The first lock is
requested to unlink an already existing inode (replacing a file) and the
second lock is requested when the destination directory needs to alloc
some space. It is rare that these two
events happen during the same rename call, and even more rare that these
two instances try to lock the same rgrp. It is, however, possible.
https://bugzilla.redhat.com/show_bug.cgi?id=404711Signed-off-by: default avatarAbhijith Das <adas@redhat.com>
Signed-off-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
parent c97bfe43
...@@ -1063,22 +1063,30 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) ...@@ -1063,22 +1063,30 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
int flags = LM_FLAG_TRY; int flags = LM_FLAG_TRY;
int skipped = 0; int skipped = 0;
int loops = 0; int loops = 0;
int error; int error, rg_locked;
/* Try recently successful rgrps */ /* Try recently successful rgrps */
rgd = recent_rgrp_first(sdp, ip->i_last_rg_alloc); rgd = recent_rgrp_first(sdp, ip->i_last_rg_alloc);
while (rgd) { while (rgd) {
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, rg_locked = 0;
LM_FLAG_TRY, &al->al_rgd_gh);
if (gfs2_glock_is_locked_by_me(rgd->rd_gl)) {
rg_locked = 1;
error = 0;
} else {
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE,
LM_FLAG_TRY, &al->al_rgd_gh);
}
switch (error) { switch (error) {
case 0: case 0:
if (try_rgrp_fit(rgd, al)) if (try_rgrp_fit(rgd, al))
goto out; goto out;
if (rgd->rd_flags & GFS2_RDF_CHECK) if (rgd->rd_flags & GFS2_RDF_CHECK)
inode = try_rgrp_unlink(rgd, last_unlinked); inode = try_rgrp_unlink(rgd, last_unlinked);
gfs2_glock_dq_uninit(&al->al_rgd_gh); if (!rg_locked)
gfs2_glock_dq_uninit(&al->al_rgd_gh);
if (inode) if (inode)
return inode; return inode;
rgd = recent_rgrp_next(rgd, 1); rgd = recent_rgrp_next(rgd, 1);
...@@ -1098,15 +1106,23 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) ...@@ -1098,15 +1106,23 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked)
begin = rgd = forward_rgrp_get(sdp); begin = rgd = forward_rgrp_get(sdp);
for (;;) { for (;;) {
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, flags, rg_locked = 0;
&al->al_rgd_gh);
if (gfs2_glock_is_locked_by_me(rgd->rd_gl)) {
rg_locked = 1;
error = 0;
} else {
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, flags,
&al->al_rgd_gh);
}
switch (error) { switch (error) {
case 0: case 0:
if (try_rgrp_fit(rgd, al)) if (try_rgrp_fit(rgd, al))
goto out; goto out;
if (rgd->rd_flags & GFS2_RDF_CHECK) if (rgd->rd_flags & GFS2_RDF_CHECK)
inode = try_rgrp_unlink(rgd, last_unlinked); inode = try_rgrp_unlink(rgd, last_unlinked);
gfs2_glock_dq_uninit(&al->al_rgd_gh); if (!rg_locked)
gfs2_glock_dq_uninit(&al->al_rgd_gh);
if (inode) if (inode)
return inode; return inode;
break; break;
...@@ -1213,7 +1229,8 @@ void gfs2_inplace_release(struct gfs2_inode *ip) ...@@ -1213,7 +1229,8 @@ void gfs2_inplace_release(struct gfs2_inode *ip)
al->al_line); al->al_line);
al->al_rgd = NULL; al->al_rgd = NULL;
gfs2_glock_dq_uninit(&al->al_rgd_gh); if (al->al_rgd_gh.gh_gl)
gfs2_glock_dq_uninit(&al->al_rgd_gh);
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);
} }
......
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