gfs2: Check for log write errors before telling dlm to unlock

Before this patch, function do_xmote just assumed all the writes
submitted to the journal were finished and successful, and it
called the go_unlock function to release the dlm lock. But if
they're not, and a revoke failed to make its way to the journal,
a journal replay on another node will cause corruption if we
let the go_inval function continue and tell dlm to release the
glock to another node. This patch adds a couple checks for errors
in do_xmote after the calls to go_sync and go_inval. If an error
is found, we cannot withdraw yet, because the withdraw itself
uses glocks to make the file system read-only. Instead, we flag
the error. Later, asserts should cause another node to replay
the journal before continuing, thus protecting rgrp and dinode
glocks and maintaining the integrity of the metadata. Note that
we only need to do this for journaled glocks. System glocks
should be able to progress even under withdrawn conditions.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Reviewed-by: Andreas Gruenbacher <agruenba@redhat.com>
This commit is contained in:
Bob Peterson 2019-04-29 13:14:58 -06:00
parent f05b86db31
commit d93ae386ef

View File

@ -622,6 +622,32 @@ __acquires(&gl->gl_lockref.lock)
} }
gfs2_glock_hold(gl); gfs2_glock_hold(gl);
/*
* Check for an error encountered since we called go_sync and go_inval.
* If so, we can't withdraw from the glock code because the withdraw
* code itself uses glocks (see function signal_our_withdraw) to
* change the mount to read-only. Most importantly, we must not call
* dlm to unlock the glock until the journal is in a known good state
* (after journal replay) otherwise other nodes may use the object
* (rgrp or dinode) and then later, journal replay will corrupt the
* file system. The best we can do here is wait for the logd daemon
* to see sd_log_error and withdraw, and in the meantime, requeue the
* work for later.
*
* However, if we're just unlocking the lock (say, for unmount, when
* gfs2_gl_hash_clear calls clear_glock) and recovery is complete
* then it's okay to tell dlm to unlock it.
*/
if (unlikely(sdp->sd_log_error && !gfs2_withdrawn(sdp)))
gfs2_withdraw_delayed(sdp);
if (glock_blocked_by_withdraw(gl)) {
if (target != LM_ST_UNLOCKED ||
test_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags)) {
gfs2_glock_queue_work(gl, GL_GLOCK_DFT_HOLD);
goto out;
}
}
if (sdp->sd_lockstruct.ls_ops->lm_lock) { if (sdp->sd_lockstruct.ls_ops->lm_lock) {
/* lock_dlm */ /* lock_dlm */
ret = sdp->sd_lockstruct.ls_ops->lm_lock(gl, target, lck_flags); ret = sdp->sd_lockstruct.ls_ops->lm_lock(gl, target, lck_flags);
@ -630,8 +656,7 @@ __acquires(&gl->gl_lockref.lock)
test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags)) { test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags)) {
finish_xmote(gl, target); finish_xmote(gl, target);
gfs2_glock_queue_work(gl, 0); gfs2_glock_queue_work(gl, 0);
} } else if (ret) {
else if (ret) {
fs_err(sdp, "lm_lock ret %d\n", ret); fs_err(sdp, "lm_lock ret %d\n", ret);
GLOCK_BUG_ON(gl, !gfs2_withdrawn(sdp)); GLOCK_BUG_ON(gl, !gfs2_withdrawn(sdp));
} }
@ -639,7 +664,7 @@ __acquires(&gl->gl_lockref.lock)
finish_xmote(gl, target); finish_xmote(gl, target);
gfs2_glock_queue_work(gl, 0); gfs2_glock_queue_work(gl, 0);
} }
out:
spin_lock(&gl->gl_lockref.lock); spin_lock(&gl->gl_lockref.lock);
} }