gfs2: Do log_flush in gfs2_ail_empty_gl even if ail list is empty
authorBob Peterson <rpeterso@redhat.com>
Wed, 13 Nov 2019 19:47:02 +0000 (13:47 -0600)
committerBob Peterson <rpeterso@redhat.com>
Thu, 27 Feb 2020 13:53:18 +0000 (07:53 -0600)
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>
fs/gfs2/glops.c
fs/gfs2/log.c
fs/gfs2/log.h

index b58924482d9abadd4a57afc0a7fd54125eef0cf2..bbbcae8d853c7feadb8a731117c0b9ae22d3ade7 100644 (file)
@@ -92,8 +92,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
@@ -108,6 +132,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);
 }
index 578c1e0cd415329fe71bd949d76ae11a4b25904f..c37f81470792b084bbe70d124261e5d40acf0778 100644 (file)
@@ -537,7 +537,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);
 
index c0a65e5a126b64feb06af184c3e6c50073f6a5df..c1cd6ae176597cd5720b7381c3b23039e5f02b5b 100644 (file)
@@ -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 int gfs2_logd(void *data);
 extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);