| From 0d0cf502c79b2137ce2bbf79d2436c8d87a87faf Mon Sep 17 00:00:00 2001 |
| From: Bob Peterson <rpeterso@redhat.com> |
| Date: Wed, 13 Nov 2019 13:47:02 -0600 |
| Subject: [PATCH] gfs2: Do log_flush in gfs2_ail_empty_gl even if ail list is |
| empty |
| |
| commit 9ff78289356af640941bbb0dd3f46af2063f0046 upstream. |
| |
| Before this patch, if gfs2_ail_empty_gl saw there was nothing on |
| the ail list, it would return and not flush the log. The problem |
| is that there could still be a revoke for the rgrp sitting on the |
| sd_log_le_revoke list that's been recently taken off the ail list. |
| But that revoke still needs to be written, and the rgrp_go_inval |
| still needs to call log_flush_wait to ensure the revokes are all |
| properly written to the journal before we relinquish control of |
| the glock to another node. If we give the glock to another node |
| before we have this knowledge, the node might crash and its journal |
| replayed, in which case the missing revoke would allow the journal |
| replay to replay the rgrp over top of the rgrp we already gave to |
| another node, thus overwriting its changes and corrupting the |
| file system. |
| |
| This patch makes gfs2_ail_empty_gl still call gfs2_log_flush rather |
| than returning. |
| |
| Signed-off-by: Bob Peterson <rpeterso@redhat.com> |
| Reviewed-by: Andreas Gruenbacher <agruenba@redhat.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c |
| index cf4c767005b1..02cb8dc2d76a 100644 |
| --- a/fs/gfs2/glops.c |
| +++ b/fs/gfs2/glops.c |
| @@ -89,8 +89,32 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl) |
| INIT_LIST_HEAD(&tr.tr_databuf); |
| tr.tr_revokes = atomic_read(&gl->gl_ail_count); |
| |
| - if (!tr.tr_revokes) |
| + if (!tr.tr_revokes) { |
| + bool have_revokes; |
| + bool log_in_flight; |
| + |
| + /* |
| + * We have nothing on the ail, but there could be revokes on |
| + * the sdp revoke queue, in which case, we still want to flush |
| + * the log and wait for it to finish. |
| + * |
| + * If the sdp revoke list is empty too, we might still have an |
| + * io outstanding for writing revokes, so we should wait for |
| + * it before returning. |
| + * |
| + * If none of these conditions are true, our revokes are all |
| + * flushed and we can return. |
| + */ |
| + gfs2_log_lock(sdp); |
| + have_revokes = !list_empty(&sdp->sd_log_revokes); |
| + log_in_flight = atomic_read(&sdp->sd_log_in_flight); |
| + gfs2_log_unlock(sdp); |
| + if (have_revokes) |
| + goto flush; |
| + if (log_in_flight) |
| + log_flush_wait(sdp); |
| return; |
| + } |
| |
| /* A shortened, inline version of gfs2_trans_begin() |
| * tr->alloced is not set since the transaction structure is |
| @@ -105,6 +129,7 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl) |
| __gfs2_ail_flush(gl, 0, tr.tr_revokes); |
| |
| gfs2_trans_end(sdp); |
| +flush: |
| gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL | |
| GFS2_LFC_AIL_EMPTY_GL); |
| } |
| diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c |
| index d2a3bfdd350f..c2b238d1a9dd 100644 |
| --- a/fs/gfs2/log.c |
| +++ b/fs/gfs2/log.c |
| @@ -513,7 +513,7 @@ static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail) |
| } |
| |
| |
| -static void log_flush_wait(struct gfs2_sbd *sdp) |
| +void log_flush_wait(struct gfs2_sbd *sdp) |
| { |
| DEFINE_WAIT(wait); |
| |
| diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h |
| index c762da494546..52b9bf27e918 100644 |
| --- a/fs/gfs2/log.h |
| +++ b/fs/gfs2/log.h |
| @@ -73,6 +73,7 @@ extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, |
| u32 type); |
| extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans); |
| extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc); |
| +extern void log_flush_wait(struct gfs2_sbd *sdp); |
| |
| extern void gfs2_log_shutdown(struct gfs2_sbd *sdp); |
| extern int gfs2_logd(void *data); |
| -- |
| 2.7.4 |
| |