diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2009-02-05 05:12:38 -0500 |
---|---|---|
committer | Steven Whitehouse <steve@dolmen.chygwyn.com> | 2009-03-24 07:21:18 -0400 |
commit | d8348de06f704fc34d24ec068546ecb1045fc11a (patch) | |
tree | e6b7eb01ec128e40b255b903e3c6629fbd8174e5 /fs/gfs2/trans.c | |
parent | e7c8707ea2b9106f0f78c43348ff5d5e82ba7961 (diff) |
GFS2: Fix deadlock on journal flush
This patch fixes a deadlock when the journal is flushed and there
are dirty inodes other than the one which caused the journal flush.
Originally the journal flushing code was trying to obtain the
transaction glock while running the flush code for an inode glock.
We no longer require the transaction glock at this point in time
since we know that any attempt to get the transaction glock from
another node will result in a journal flush. So if we are flushing
the journal, we can be sure that the transaction lock is still
cached from when the transaction was started.
By inlining a version of gfs2_trans_begin() (minus the bit which
gets the transaction glock) we can avoid the deadlock problems
caused if there is a demote request queued up on the transaction
glock.
In addition I've also moved the umount rwsem so that it covers
the glock workqueue, since it all demotions are done by this
workqueue now. That fixes a bug on umount which I came across
while fixing the original problem.
Reported-by: David Teigland <teigland@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/gfs2/trans.c')
-rw-r--r-- | fs/gfs2/trans.c | 16 |
1 files changed, 10 insertions, 6 deletions
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c index 33cd523ec97e..053752d4b27f 100644 --- a/fs/gfs2/trans.c +++ b/fs/gfs2/trans.c | |||
@@ -87,9 +87,11 @@ void gfs2_trans_end(struct gfs2_sbd *sdp) | |||
87 | 87 | ||
88 | if (!tr->tr_touched) { | 88 | if (!tr->tr_touched) { |
89 | gfs2_log_release(sdp, tr->tr_reserved); | 89 | gfs2_log_release(sdp, tr->tr_reserved); |
90 | gfs2_glock_dq(&tr->tr_t_gh); | 90 | if (tr->tr_t_gh.gh_gl) { |
91 | gfs2_holder_uninit(&tr->tr_t_gh); | 91 | gfs2_glock_dq(&tr->tr_t_gh); |
92 | kfree(tr); | 92 | gfs2_holder_uninit(&tr->tr_t_gh); |
93 | kfree(tr); | ||
94 | } | ||
93 | return; | 95 | return; |
94 | } | 96 | } |
95 | 97 | ||
@@ -105,9 +107,11 @@ void gfs2_trans_end(struct gfs2_sbd *sdp) | |||
105 | } | 107 | } |
106 | 108 | ||
107 | gfs2_log_commit(sdp, tr); | 109 | gfs2_log_commit(sdp, tr); |
108 | gfs2_glock_dq(&tr->tr_t_gh); | 110 | if (tr->tr_t_gh.gh_gl) { |
109 | gfs2_holder_uninit(&tr->tr_t_gh); | 111 | gfs2_glock_dq(&tr->tr_t_gh); |
110 | kfree(tr); | 112 | gfs2_holder_uninit(&tr->tr_t_gh); |
113 | kfree(tr); | ||
114 | } | ||
111 | 115 | ||
112 | if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS) | 116 | if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS) |
113 | gfs2_log_flush(sdp, NULL); | 117 | gfs2_log_flush(sdp, NULL); |