Commit 72dbf479 authored by Bob Peterson's avatar Bob Peterson Committed by Steven Whitehouse

GFS2: rm on multiple nodes causes panic

This patch fixes a problem whereby simultaneous unlink, rmdir,
rename and link operations (e.g. rm -fR *) from multiple nodes
on the same GFS2 file system can cause kernel panics, hangs,
and/or memory corruption.  It also gets rid of all the non-rgrp
calls to gfs2_glock_nq_m.
Signed-off-by: default avatarBob Peterson <rpeterso@redhat.com>
Signed-off-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
parent 9b8df98f
...@@ -159,9 +159,13 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, ...@@ -159,9 +159,13 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1); gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
error = gfs2_glock_nq_m(2, ghs); error = gfs2_glock_nq(ghs); /* parent */
if (error) if (error)
goto out; goto out_parent;
error = gfs2_glock_nq(ghs + 1); /* child */
if (error)
goto out_child;
error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC); error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC);
if (error) if (error)
...@@ -245,8 +249,10 @@ out_alloc: ...@@ -245,8 +249,10 @@ out_alloc:
if (alloc_required) if (alloc_required)
gfs2_alloc_put(dip); gfs2_alloc_put(dip);
out_gunlock: out_gunlock:
gfs2_glock_dq_m(2, ghs); gfs2_glock_dq(ghs + 1);
out: out_child:
gfs2_glock_dq(ghs);
out_parent:
gfs2_holder_uninit(ghs); gfs2_holder_uninit(ghs);
gfs2_holder_uninit(ghs + 1); gfs2_holder_uninit(ghs + 1);
if (!error) { if (!error) {
...@@ -302,7 +308,7 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) ...@@ -302,7 +308,7 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
error = gfs2_unlink_ok(dip, &dentry->d_name, ip); error = gfs2_unlink_ok(dip, &dentry->d_name, ip);
if (error) if (error)
goto out_rgrp; goto out_gunlock;
error = gfs2_trans_begin(sdp, 2*RES_DINODE + RES_LEAF + RES_RG_BIT, 0); error = gfs2_trans_begin(sdp, 2*RES_DINODE + RES_LEAF + RES_RG_BIT, 0);
if (error) if (error)
...@@ -316,6 +322,7 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) ...@@ -316,6 +322,7 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
out_end_trans: out_end_trans:
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
out_gunlock:
gfs2_glock_dq(ghs + 2); gfs2_glock_dq(ghs + 2);
out_rgrp: out_rgrp:
gfs2_holder_uninit(ghs + 2); gfs2_holder_uninit(ghs + 2);
...@@ -485,7 +492,6 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -485,7 +492,6 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry)
struct gfs2_holder ri_gh; struct gfs2_holder ri_gh;
int error; int error;
error = gfs2_rindex_hold(sdp, &ri_gh); error = gfs2_rindex_hold(sdp, &ri_gh);
if (error) if (error)
return error; return error;
...@@ -495,9 +501,17 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -495,9 +501,17 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry)
rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr); rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr);
gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2); gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2);
error = gfs2_glock_nq_m(3, ghs); error = gfs2_glock_nq(ghs); /* parent */
if (error) if (error)
goto out; goto out_parent;
error = gfs2_glock_nq(ghs + 1); /* child */
if (error)
goto out_child;
error = gfs2_glock_nq(ghs + 2); /* rgrp */
if (error)
goto out_rgrp;
error = gfs2_unlink_ok(dip, &dentry->d_name, ip); error = gfs2_unlink_ok(dip, &dentry->d_name, ip);
if (error) if (error)
...@@ -523,11 +537,15 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -523,11 +537,15 @@ static int gfs2_rmdir(struct inode *dir, struct dentry *dentry)
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
out_gunlock: out_gunlock:
gfs2_glock_dq_m(3, ghs); gfs2_glock_dq(ghs + 2);
out: out_rgrp:
gfs2_holder_uninit(ghs);
gfs2_holder_uninit(ghs + 1);
gfs2_holder_uninit(ghs + 2); gfs2_holder_uninit(ghs + 2);
gfs2_glock_dq(ghs + 1);
out_child:
gfs2_holder_uninit(ghs + 1);
gfs2_glock_dq(ghs);
out_parent:
gfs2_holder_uninit(ghs);
gfs2_glock_dq_uninit(&ri_gh); gfs2_glock_dq_uninit(&ri_gh);
return error; return error;
} }
...@@ -639,9 +657,11 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, ...@@ -639,9 +657,11 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++); gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++);
} }
error = gfs2_glock_nq_m(num_gh, ghs); for (x = 0; x < num_gh; x++) {
if (error) error = gfs2_glock_nq(ghs + x);
goto out_uninit; if (error)
goto out_gunlock;
}
/* Check out the old directory */ /* Check out the old directory */
...@@ -804,10 +824,10 @@ out_alloc: ...@@ -804,10 +824,10 @@ out_alloc:
if (alloc_required) if (alloc_required)
gfs2_alloc_put(ndip); gfs2_alloc_put(ndip);
out_gunlock: out_gunlock:
gfs2_glock_dq_m(num_gh, ghs); while (x--) {
out_uninit: gfs2_glock_dq(ghs + x);
for (x = 0; x < num_gh; x++)
gfs2_holder_uninit(ghs + x); gfs2_holder_uninit(ghs + x);
}
out_gunlock_r: out_gunlock_r:
if (dir_rename) if (dir_rename)
gfs2_glock_dq_uninit(&r_gh); gfs2_glock_dq_uninit(&r_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