diff options
author | S. Wendy Cheng <wcheng@redhat.com> | 2007-01-18 15:56:34 -0500 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2007-02-05 13:36:15 -0500 |
commit | 5509826f1e548d14bb888c1cb6e3bbf23f855770 (patch) | |
tree | 20915fb965f5895f3a41361bdb6182ae10db242c /fs/gfs2 | |
parent | e1d5b18ae92d0bbfe66dc2b4bab65006d32c5f7d (diff) |
[GFS2] Fix change nlink deadlock
Bugzilla 215088
Fix deadlock in gfs2_change_nlink() while installing RHEL5 into GFS2
partition. The gfs2_rename() apparently needs block allocation for the
new name (into the directory) where it requires rg locks. At the same
time, while updating the nlink count for the replaced file,
gfs2_change_nlink() tries to return the inode meta-data back to resource
group where it needs rg locks too. Our logic doesn't allow process to
acquire these locks recursively by the same process (RHEL installer)
that results a BUG call. This only happens within rename code path and
only if the destination file exists before the rename operation.
Signed-off-by: S. Wendy Cheng <wcheng@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/gfs2')
-rw-r--r-- | fs/gfs2/inode.c | 20 | ||||
-rw-r--r-- | fs/gfs2/inode.h | 1 | ||||
-rw-r--r-- | fs/gfs2/ops_inode.c | 25 |
3 files changed, 39 insertions, 7 deletions
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index d122074c45e1..6bc443644c3c 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c | |||
@@ -281,16 +281,14 @@ out: | |||
281 | } | 281 | } |
282 | 282 | ||
283 | /** | 283 | /** |
284 | * gfs2_change_nlink - Change nlink count on inode | 284 | * gfs2_change_nlink_i - Change nlink count on inode |
285 | * @ip: The GFS2 inode | 285 | * @ip: The GFS2 inode |
286 | * @diff: The change in the nlink count required | 286 | * @diff: The change in the nlink count required |
287 | * | 287 | * |
288 | * Returns: errno | 288 | * Returns: errno |
289 | */ | 289 | */ |
290 | 290 | int gfs2_change_nlink_i(struct gfs2_inode *ip, int diff) | |
291 | int gfs2_change_nlink(struct gfs2_inode *ip, int diff) | ||
292 | { | 291 | { |
293 | struct gfs2_sbd *sdp = ip->i_inode.i_sb->s_fs_info; | ||
294 | struct buffer_head *dibh; | 292 | struct buffer_head *dibh; |
295 | u32 nlink; | 293 | u32 nlink; |
296 | int error; | 294 | int error; |
@@ -322,6 +320,20 @@ int gfs2_change_nlink(struct gfs2_inode *ip, int diff) | |||
322 | brelse(dibh); | 320 | brelse(dibh); |
323 | mark_inode_dirty(&ip->i_inode); | 321 | mark_inode_dirty(&ip->i_inode); |
324 | 322 | ||
323 | return error; | ||
324 | } | ||
325 | |||
326 | int gfs2_change_nlink(struct gfs2_inode *ip, int diff) | ||
327 | { | ||
328 | struct gfs2_sbd *sdp = ip->i_inode.i_sb->s_fs_info; | ||
329 | int error; | ||
330 | |||
331 | /* update the nlink */ | ||
332 | error = gfs2_change_nlink_i(ip, diff); | ||
333 | if (error) | ||
334 | return error; | ||
335 | |||
336 | /* return meta data block back to rg */ | ||
325 | if (ip->i_inode.i_nlink == 0) { | 337 | if (ip->i_inode.i_nlink == 0) { |
326 | struct gfs2_rgrpd *rgd; | 338 | struct gfs2_rgrpd *rgd; |
327 | struct gfs2_holder ri_gh, rg_gh; | 339 | struct gfs2_holder ri_gh, rg_gh; |
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index b57f448b15bc..85c67cb568d1 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h | |||
@@ -40,6 +40,7 @@ int gfs2_inode_refresh(struct gfs2_inode *ip); | |||
40 | 40 | ||
41 | int gfs2_dinode_dealloc(struct gfs2_inode *inode); | 41 | int gfs2_dinode_dealloc(struct gfs2_inode *inode); |
42 | int gfs2_change_nlink(struct gfs2_inode *ip, int diff); | 42 | int gfs2_change_nlink(struct gfs2_inode *ip, int diff); |
43 | int gfs2_change_nlink_i(struct gfs2_inode *ip, int diff); | ||
43 | struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, | 44 | struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, |
44 | int is_root, struct nameidata *nd); | 45 | int is_root, struct nameidata *nd); |
45 | struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, | 46 | struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, |
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 636dda4c7d38..919e8947e710 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c | |||
@@ -553,6 +553,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | |||
553 | int alloc_required; | 553 | int alloc_required; |
554 | unsigned int x; | 554 | unsigned int x; |
555 | int error; | 555 | int error; |
556 | struct gfs2_rgrpd *rgd; | ||
556 | 557 | ||
557 | if (ndentry->d_inode) { | 558 | if (ndentry->d_inode) { |
558 | nip = GFS2_I(ndentry->d_inode); | 559 | nip = GFS2_I(ndentry->d_inode); |
@@ -684,12 +685,12 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | |||
684 | error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + | 685 | error = gfs2_trans_begin(sdp, sdp->sd_max_dirres + |
685 | al->al_rgd->rd_ri.ri_length + | 686 | al->al_rgd->rd_ri.ri_length + |
686 | 4 * RES_DINODE + 4 * RES_LEAF + | 687 | 4 * RES_DINODE + 4 * RES_LEAF + |
687 | RES_STATFS + RES_QUOTA, 0); | 688 | RES_STATFS + RES_QUOTA + 1, 0); |
688 | if (error) | 689 | if (error) |
689 | goto out_ipreserv; | 690 | goto out_ipreserv; |
690 | } else { | 691 | } else { |
691 | error = gfs2_trans_begin(sdp, 4 * RES_DINODE + | 692 | error = gfs2_trans_begin(sdp, 4 * RES_DINODE + |
692 | 5 * RES_LEAF, 0); | 693 | 5 * RES_LEAF + 1, 0); |
693 | if (error) | 694 | if (error) |
694 | goto out_gunlock; | 695 | goto out_gunlock; |
695 | } | 696 | } |
@@ -703,7 +704,25 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | |||
703 | error = gfs2_dir_del(ndip, &ndentry->d_name); | 704 | error = gfs2_dir_del(ndip, &ndentry->d_name); |
704 | if (error) | 705 | if (error) |
705 | goto out_end_trans; | 706 | goto out_end_trans; |
706 | error = gfs2_change_nlink(nip, -1); | 707 | error = gfs2_change_nlink_i(nip, -1); |
708 | if ((!error) && (nip->i_inode.i_nlink == 0)) { | ||
709 | error = -EIO; | ||
710 | rgd = gfs2_blk2rgrpd(sdp, nip->i_num.no_addr); | ||
711 | if (rgd) { | ||
712 | struct gfs2_holder nlink_rg_gh; | ||
713 | if (rgd != nip->i_alloc.al_rgd) | ||
714 | error = gfs2_glock_nq_init( | ||
715 | rgd->rd_gl, LM_ST_EXCLUSIVE, | ||
716 | 0, &nlink_rg_gh); | ||
717 | else | ||
718 | error = 0; | ||
719 | if (!error) { | ||
720 | gfs2_unlink_di(&nip->i_inode); | ||
721 | if (rgd != nip->i_alloc.al_rgd) | ||
722 | gfs2_glock_dq_uninit(&nlink_rg_gh); | ||
723 | } | ||
724 | } | ||
725 | } | ||
707 | } | 726 | } |
708 | if (error) | 727 | if (error) |
709 | goto out_end_trans; | 728 | goto out_end_trans; |