gfs2 filesystem and dlm development
 help / color / mirror / Atom feed
From: Andreas Gruenbacher <agruenba@redhat.com>
To: gfs2@lists.linux.dev
Cc: Andreas Gruenbacher <agruenba@redhat.com>
Subject: [PATCH 05/18] gfs2: Fix data loss during inode evict
Date: Mon, 13 Apr 2026 16:51:58 +0200	[thread overview]
Message-ID: <20260413145211.881752-6-agruenba@redhat.com> (raw)
In-Reply-To: <20260413145211.881752-1-agruenba@redhat.com>

When gfs2_evict_inode() is called on an inode with unwritten data in the
page cache, the page cache needs to be written before it can be
truncated.  This doesn't always happen.  Fix that by changing
gfs2_evict_inode() to always either call evict_linked_inode() or
evict_unlinked_inode().

Inside evict_unlinked_inode(), first check if the inode is dirty.  If it
is, make sure the inode glock is held and write back the data and
metadata.  If it isn't, skip those steps.

Also, make sure that gfs2_evict_inode() calls gfs2_evict_inode() and
evict_unlinked_inode() only if ip->i_gl is not NULL; this avoids
unnecessary complications there.

Fixes xfstest generic/211.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/gfs2/super.c | 36 ++++++++++++++++++++++++++++--------
 1 file changed, 28 insertions(+), 8 deletions(-)

diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index b20494d6730a..e4219a04d16e 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -1241,6 +1241,9 @@ static enum evict_behavior evict_should_delete(struct inode *inode,
 	struct gfs2_sbd *sdp = sb->s_fs_info;
 	int ret;
 
+	if (inode->i_nlink)
+		return EVICT_SHOULD_SKIP_DELETE;
+
 	if (gfs2_holder_initialized(&ip->i_iopen_gh) &&
 	    test_bit(GLF_DEFER_DELETE, &ip->i_iopen_gh.gh_gl->gl_flags))
 		return EVICT_SHOULD_DEFER_DELETE;
@@ -1279,13 +1282,18 @@ static enum evict_behavior evict_should_delete(struct inode *inode,
 /**
  * evict_unlinked_inode - delete the pieces of an unlinked evicted inode
  * @inode: The inode to evict
+ * @gh: The glock holder structure
  */
-static int evict_unlinked_inode(struct inode *inode)
+static int evict_unlinked_inode(struct inode *inode, struct gfs2_holder *gh)
 {
 	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_glock *gl = ip->i_gl;
 	int ret;
 
+	/* The inode glock must be held exclusively and be instantiated. */
+	BUG_ON(!gfs2_holder_initialized(gh) ||
+	       test_bit(GLF_INSTANTIATE_NEEDED, &gl->gl_flags));
+
 	if (S_ISDIR(inode->i_mode) &&
 	    (ip->i_diskflags & GFS2_DIF_EXHASH)) {
 		ret = gfs2_dir_exhash_dealloc(ip);
@@ -1318,7 +1326,7 @@ static int evict_unlinked_inode(struct inode *inode)
 	 */
 
 	ret = gfs2_dinode_dealloc(ip);
-	if (!ret && gl)
+	if (!ret)
 		gfs2_inode_remember_delete(gl, ip->i_no_formal_ino);
 
 out:
@@ -1357,19 +1365,30 @@ static int gfs2_truncate_inode_pages(struct inode *inode)
 /*
  * evict_linked_inode - evict an inode whose dinode has not been unlinked
  * @inode: The inode to evict
+ * @gh: The glock holder structure
  */
-static int evict_linked_inode(struct inode *inode)
+static int evict_linked_inode(struct inode *inode, struct gfs2_holder *gh)
 {
 	struct super_block *sb = inode->i_sb;
 	struct gfs2_sbd *sdp = sb->s_fs_info;
 	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_glock *gl = ip->i_gl;
-	struct address_space *metamapping;
+	struct address_space *metamapping = gfs2_glock2aspace(gl);
 	int ret;
 
+	if (!(test_bit(GLF_DIRTY, &gl->gl_flags) || inode->i_flags & I_DIRTY))
+		goto clean;
+
+	/* The inode glock must be held exclusively and be instantiated. */
+	if (!gfs2_holder_initialized(gh))
+		ret = gfs2_glock_nq_init(gl, LM_ST_EXCLUSIVE, 0, gh);
+	else
+		ret = gfs2_instantiate(gh);
+	if (ret)
+		return ret;
+
 	gfs2_log_flush(sdp, gl, GFS2_LOG_HEAD_FLUSH_NORMAL |
 		       GFS2_LFC_EVICT_INODE);
-	metamapping = gfs2_glock2aspace(gl);
 	if (test_bit(GLF_DIRTY, &gl->gl_flags)) {
 		filemap_fdatawrite(metamapping);
 		filemap_fdatawait(metamapping);
@@ -1377,6 +1396,7 @@ static int evict_linked_inode(struct inode *inode)
 	write_inode_now(inode, 1);
 	gfs2_ail_flush(gl, 0);
 
+clean:
 	ret = gfs2_truncate_inode_pages(inode);
 	if (ret)
 		return ret;
@@ -1415,7 +1435,7 @@ static void gfs2_evict_inode(struct inode *inode)
 	int ret;
 
 	gfs2_holder_mark_uninitialized(&gh);
-	if (inode->i_nlink || sb_rdonly(sb) || !ip->i_no_addr)
+	if (sb_rdonly(sb) || !ip->i_no_addr || !ip->i_gl)
 		goto out;
 
 	/*
@@ -1440,9 +1460,9 @@ static void gfs2_evict_inode(struct inode *inode)
 		behavior = EVICT_SHOULD_SKIP_DELETE;
 	}
 	if (behavior == EVICT_SHOULD_DELETE)
-		ret = evict_unlinked_inode(inode);
+		ret = evict_unlinked_inode(inode, &gh);
 	else
-		ret = evict_linked_inode(inode);
+		ret = evict_linked_inode(inode, &gh);
 
 	if (gfs2_rs_active(&ip->i_res))
 		gfs2_rs_deltree(&ip->i_res);
-- 
2.53.0


  parent reply	other threads:[~2026-04-13 14:52 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-13 14:51 [PATCH 00/18] gfs2 patches on for-next Andreas Gruenbacher
2026-04-13 14:51 ` [PATCH 01/18] gfs2: Call unlock_new_inode before d_instantiate Andreas Gruenbacher
2026-04-13 14:51 ` [PATCH 02/18] gfs2: Remove unnecessary check in gfs2_evict_inode Andreas Gruenbacher
2026-04-13 14:51 ` [PATCH 03/18] gfs2: Avoid unnecessary transactions in evict_linked_inode Andreas Gruenbacher
2026-04-13 14:51 ` [PATCH 04/18] gfs2: minor evict_[un]linked_inode cleanup Andreas Gruenbacher
2026-04-13 14:51 ` Andreas Gruenbacher [this message]
2026-04-13 14:51 ` [PATCH 06/18] gfs2: less aggressive low-memory log flushing Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 07/18] gfs2: Get rid of gfs2_log_[un]lock helpers Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 08/18] gfs2: Move gfs2_remove_from_journal to log.c Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 09/18] gfs2: Remove trans_drain code duplication Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 10/18] gfs2: bufdata allocation race Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 11/18] gfs2: drain ail under sd_log_flush_lock Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 12/18] gfs2: fix address space truncation during withdraw Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 13/18] gfs2: add some missing log locking Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 14/18] gfs2: gfs2_log_flush withdraw fixes Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 15/18] gfs2: inode directory consistency checks Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 16/18] gfs2: wait for withdraw earlier during unmount Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 17/18] gfs2: hide error messages after withdraw Andreas Gruenbacher
2026-04-13 14:52 ` [PATCH 18/18] gfs2: prevent NULL pointer dereference during unmount Andreas Gruenbacher

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=20260413145211.881752-6-agruenba@redhat.com \
    --to=agruenba@redhat.com \
    --cc=gfs2@lists.linux.dev \
    /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