cluster-devel.redhat.com archive mirror
 help / color / mirror / Atom feed
From: Bob Peterson <rpeterso@redhat.com>
To: cluster-devel.redhat.com
Subject: [Cluster-devel] [GFS2 PATCH 08/28] gfs2: Rework how rgrp buffer_heads are managed
Date: Thu, 20 Feb 2020 13:53:09 -0600	[thread overview]
Message-ID: <20200220195329.952027-9-rpeterso@redhat.com> (raw)
In-Reply-To: <20200220195329.952027-1-rpeterso@redhat.com>

Before this patch, the rgrp code had a serious problem related to
how it managed buffer_heads for resource groups. The problem caused
file system corruption, especially in cases of journal replay.

When an rgrp glock was demoted to transfer ownership to a
different cluster node, do_xmote() first calls rgrp_go_sync and then
rgrp_go_inval, as expected. When it calls rgrp_go_sync, that called
gfs2_rgrp_brelse() that dropped the buffer_head reference count.
In most cases, the reference count went to zero, which is right.
However, there were other places where the buffers are handled
differently.

After rgrp_go_sync, do_xmote called rgrp_go_inval which called
gfs2_rgrp_brelse a second time, then rgrp_go_inval's call to
truncate_inode_pages_range would get rid of the pages in memory,
but only if the reference count drops to 0.

Unfortunately, gfs2_rgrp_brelse was setting bi->bi_bh = NULL.
So when rgrp_go_sync called gfs2_rgrp_brelse, it lost the pointer
to the buffer_heads in cases where the reference count was still 1.
Therefore, when rgrp_go_inval called gfs2_rgrp_brelse a second time,
it failed the check for "if (bi->bi_bh)" and thus failed to call
brelse a second time. Because of that, the reference count on those
buffers sometimes failed to drop from 1 to 0. And that caused
function truncate_inode_pages_range to keep the pages in page cache
rather than freeing them.

The next time the rgrp glock was acquired, the metadata read of
the rgrp buffers re-used the pages in memory, which were now
wrong because they were likely modified by the other node who
acquired the glock in EX (which is why we demoted the glock).
This re-use of the page cache caused corruption because changes
made by the other nodes were never seen, so the bitmaps were
inaccurate.

For some reason, the problem became most apparent when journal
replay forced the replay of rgrps in memory, which caused newer
rgrp data to be overwritten by the older in-core pages.

A big part of the problem was that the rgrp buffer were released
in multiple places: The go_unlock function would release them when
the glock was released rather than when the glock is demoted,
which is clearly wrong because our intent was to cache them until
the glock is demoted from SH or EX.

This patch attempts to clean up the mess and make one consistent
and centralized mechanism for managing the rgrp buffer_heads by
implementing several changes:

1. It eliminates the call to gfs2_rgrp_brelse() from rgrp_go_sync.
   We don't want to release the buffers or zero the pointers when
   syncing for the reasons stated above. It only makes sense to
   release them when the glock is actually invalidated (go_inval).
   And when we do, then we set the bh pointers to NULL.
2. The go_unlock function (which was only used for rgrps) is
   eliminated, as we've talked about doing many times before.
   The go_unlock function was called too early in the glock dq
   process, and should not happen until the glock is invalidated.
3. It also eliminates the call to rgrp_brelse in gfs2_clear_rgrpd.
   That will now happen automatically when the rgrp glocks are
   demoted, and shouldn't happen any sooner or later than that.
   Instead, function gfs2_clear_rgrpd has been modified to demote
   the rgrp glocks, and therefore, free those pages, before the
   remaining glocks are culled by gfs2_gl_hash_clear. This
   prevents the gl_object from hanging around when the glocks are
   culled.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Reviewed-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/gfs2/glock.c  |  8 --------
 fs/gfs2/glops.c  |  9 +--------
 fs/gfs2/incore.h |  1 -
 fs/gfs2/rgrp.c   | 23 +++++------------------
 fs/gfs2/rgrp.h   |  1 -
 5 files changed, 6 insertions(+), 36 deletions(-)

diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index d0eceaff3cea..1cb471a8bc87 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -1241,7 +1241,6 @@ int gfs2_glock_poll(struct gfs2_holder *gh)
 void gfs2_glock_dq(struct gfs2_holder *gh)
 {
 	struct gfs2_glock *gl = gh->gh_gl;
-	const struct gfs2_glock_operations *glops = gl->gl_ops;
 	unsigned delay = 0;
 	int fast_path = 0;
 
@@ -1252,13 +1251,6 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
 	list_del_init(&gh->gh_list);
 	clear_bit(HIF_HOLDER, &gh->gh_iflags);
 	if (find_first_holder(gl) == NULL) {
-		if (glops->go_unlock) {
-			GLOCK_BUG_ON(gl, test_and_set_bit(GLF_LOCK, &gl->gl_flags));
-			spin_unlock(&gl->gl_lockref.lock);
-			glops->go_unlock(gh);
-			spin_lock(&gl->gl_lockref.lock);
-			clear_bit(GLF_LOCK, &gl->gl_flags);
-		}
 		if (list_empty(&gl->gl_holders) &&
 		    !test_bit(GLF_PENDING_DEMOTE, &gl->gl_flags) &&
 		    !test_bit(GLF_DEMOTE, &gl->gl_flags))
diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 58431f67665e..1c557457c753 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -145,15 +145,9 @@ static void rgrp_go_sync(struct gfs2_glock *gl)
 {
 	struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
 	struct address_space *mapping = &sdp->sd_aspace;
-	struct gfs2_rgrpd *rgd;
+	struct gfs2_rgrpd *rgd = gfs2_glock2rgrp(gl);
 	int error;
 
-	spin_lock(&gl->gl_lockref.lock);
-	rgd = gl->gl_object;
-	if (rgd)
-		gfs2_rgrp_brelse(rgd);
-	spin_unlock(&gl->gl_lockref.lock);
-
 	if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags))
 		return;
 	GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE);
@@ -601,7 +595,6 @@ const struct gfs2_glock_operations gfs2_rgrp_glops = {
 	.go_sync = rgrp_go_sync,
 	.go_inval = rgrp_go_inval,
 	.go_lock = gfs2_rgrp_go_lock,
-	.go_unlock = gfs2_rgrp_go_unlock,
 	.go_dump = gfs2_rgrp_dump,
 	.go_type = LM_TYPE_RGRP,
 	.go_flags = GLOF_LVB,
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 3cd2de3db40a..b95c8a31d309 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -239,7 +239,6 @@ struct gfs2_glock_operations {
 	void (*go_inval) (struct gfs2_glock *gl, int flags);
 	int (*go_demote_ok) (const struct gfs2_glock *gl);
 	int (*go_lock) (struct gfs2_holder *gh);
-	void (*go_unlock) (struct gfs2_holder *gh);
 	void (*go_dump)(struct seq_file *seq, struct gfs2_glock *gl,
 			const char *fs_id_buf);
 	void (*go_callback)(struct gfs2_glock *gl, bool remote);
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index 2bdd662deff5..2ee2f7d48bc1 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -720,8 +720,12 @@ void gfs2_clear_rgrpd(struct gfs2_sbd *sdp)
 		rb_erase(n, &sdp->sd_rindex_tree);
 
 		if (gl) {
-			glock_clear_object(gl, rgd);
+			if (gl->gl_state != LM_ST_UNLOCKED) {
+				gfs2_glock_cb(gl, LM_ST_UNLOCKED);
+				flush_delayed_work(&gl->gl_work);
+			}
 			gfs2_rgrp_brelse(rgd);
+			glock_clear_object(gl, rgd);
 			gfs2_glock_put(gl);
 		}
 
@@ -1284,23 +1288,6 @@ void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd)
 			bi->bi_bh = NULL;
 		}
 	}
-
-}
-
-/**
- * gfs2_rgrp_go_unlock - Unlock a rgrp glock
- * @gh: The glock holder for the resource group
- *
- */
-
-void gfs2_rgrp_go_unlock(struct gfs2_holder *gh)
-{
-	struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object;
-	int demote_requested = test_bit(GLF_DEMOTE, &gh->gh_gl->gl_flags) |
-		test_bit(GLF_PENDING_DEMOTE, &gh->gh_gl->gl_flags);
-
-	if (rgd && demote_requested)
-		gfs2_rgrp_brelse(rgd);
 }
 
 int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h
index c14a673ae36f..a584f3096418 100644
--- a/fs/gfs2/rgrp.h
+++ b/fs/gfs2/rgrp.h
@@ -33,7 +33,6 @@ extern int gfs2_rindex_update(struct gfs2_sbd *sdp);
 extern void gfs2_free_clones(struct gfs2_rgrpd *rgd);
 extern int gfs2_rgrp_go_lock(struct gfs2_holder *gh);
 extern void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd);
-extern void gfs2_rgrp_go_unlock(struct gfs2_holder *gh);
 
 extern struct gfs2_alloc *gfs2_alloc_get(struct gfs2_inode *ip);
 
-- 
2.24.1



  parent reply	other threads:[~2020-02-20 19:53 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-20 19:53 [Cluster-devel] [GFS2 PATCH 00/28] GFS2 recovery patches v10 Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 01/28] gfs2: Split gfs2_lm_withdraw into two functions Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 02/28] gfs2: Report errors before withdraw Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 03/28] gfs2: Remove usused cluster_wide arguments of gfs2_consist functions Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 04/28] gfs2: Turn gfs2_consist into void functions Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 05/28] gfs2: Return bool from gfs2_assert functions Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 06/28] gfs2: Introduce concept of a pending withdraw Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 07/28] gfs2: clear ail1 list when gfs2 withdraws Bob Peterson
2020-02-20 19:53 ` Bob Peterson [this message]
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 09/28] gfs2: log error reform Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 10/28] gfs2: Only complain the first time an io error occurs in quota or log Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 11/28] gfs2: Ignore dlm recovery requests if gfs2 is withdrawn Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 12/28] gfs2: move check_journal_clean to util.c for future use Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 13/28] gfs2: Allow some glocks to be used during withdraw Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 14/28] gfs2: Force withdraw to replay journals and wait for it to finish Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 15/28] gfs2: fix infinite loop when checking ail item count before go_inval Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 16/28] gfs2: Add verbose option to check_journal_clean Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 17/28] gfs2: Issue revokes more intelligently Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 18/28] gfs2: Prepare to withdraw as soon as an IO error occurs in log write Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 19/28] gfs2: Check for log write errors before telling dlm to unlock Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 20/28] gfs2: Do log_flush in gfs2_ail_empty_gl even if ail list is empty Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 21/28] gfs2: Withdraw in gfs2_ail1_flush if write_cache_pages fails Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 22/28] gfs2: drain the ail2 list after io errors Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 23/28] gfs2: Don't demote a glock until its revokes are written Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 24/28] gfs2: Do proper error checking for go_sync family of glops functions Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 25/28] gfs2: flesh out delayed withdraw for gfs2_log_flush Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 26/28] fs: clean up __block_commit_write Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 27/28] gfs2: don't allow releasepage to free bd still used for revokes Bob Peterson
2020-02-20 19:53 ` [Cluster-devel] [GFS2 PATCH 28/28] gfs2: allow journal replay to hold sd_log_flush_lock Bob Peterson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200220195329.952027-9-rpeterso@redhat.com \
    --to=rpeterso@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).