Linux kernel -stable discussions
 help / color / mirror / Atom feed
From: Joseph Qi <joseph.qi@linux.alibaba.com>
To: Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
Cc: stable@vger.kernel.org
Subject: [PATCH 6.6.y] smb: client: base punch-hole EOF decision on server_eof
Date: Tue,  3 Nov 2026 13:59:23 +0800	[thread overview]
Message-ID: <20261103055923.2003159-1-joseph.qi@linux.alibaba.com> (raw)

smb3_punch_hole() decides whether to move the server EOF marker by
comparing against netfs_inode(inode)->remote_i_size.  Nothing on this
base keeps that field up to date: netfs_inode_init() has no caller under
fs/smb/, so remote_i_size is never even initialized, and the only writes
to it are netfs_resize_file() from the clone and copy_file_range paths
and this function itself.  The writeback path tracks the server size in
cifsi->server_eof through cifs_update_eof() instead, and
cifs_fattr_to_inode() refreshes only server_eof as well.

Reading remote_i_size therefore compares the punch against whatever the
slab object last held.  With it at zero, punching the first 4k of a clean
8k file passes "end > remote_i_size && i_size > remote_i_size", issues
SMB2_set_eof(4096) and truncates the server file in half, with nothing
dirty left to write the tail back.

The flush added by commit 216e46958605 ("smb/client: flush dirty data
before punching a hole") makes a second variant reachable.  It writes
out and cleans any page straddling the end of the punched range,
including the valid data in it beyond the hole, and advances server_eof
but not remote_i_size.  When the punch end lies beyond that stale value,
SMB2_set_eof() shrinks the server to the end of the hole and discards
what the flush just wrote out.  The page is clean, so a later fsync or
close will not write it again and the data is silently lost.

Base the decision on CIFS_I(inode)->server_eof and keep both size fields
in sync when the marker moves, so the server is never shrunk below data
that has already been written out.  Take inode->i_lock for the read and
the update, as server_eof is documented to require, and only ever
advance it, matching cifs_update_eof(): background writeback of folios
outside the punched range is excluded by neither i_rwsem nor the
invalidate lock, and can publish a larger EOF while SMB2_set_eof() is in
flight.

Mainline does not need this change.  cifs_alloc_inode() zeroes
_remote_i_size there, cifs_fattr_to_inode() refreshes it, and the
writeback path advances it through netfs_resize_file(), so the value
cannot be stale after the flush.

Fixes: 288d398df80c ("cifs: Fix FALLOC_FL_PUNCH_HOLE support")
Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
---
 fs/smb/client/smb2ops.c | 39 ++++++++++++++++++++++++++++++---------
 1 file changed, 30 insertions(+), 9 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 21a2b265ae7c4..2826a60f0d8ab 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3346,7 +3346,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
 	struct inode *inode = file_inode(file);
 	struct cifsFileInfo *cfile = file->private_data;
 	struct file_zero_data_information fsctl_buf;
-	unsigned long long end = offset + len, i_size, remote_i_size;
+	unsigned long long end = offset + len, i_size, server_eof;
 	long rc;
 	unsigned int xid;
 	__u8 set_sparse = 1;
@@ -3392,20 +3392,41 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
 	if (rc)
 		goto unlock;
 
-	/* If there's dirty data in the buffer that would extend the EOF if it
-	 * were written, then we need to move the EOF marker over to the lower
-	 * of the high end of the hole and the proposed EOF.  The problem is
-	 * that we locally hole-punch the tail of the dirty data, the proposed
-	 * EOF update will end up in the wrong place.
+	/*
+	 * If the tail of the file beyond the hole has not been written out
+	 * yet, the server EOF marker is still below both the end of the hole
+	 * and i_size.  Move it up to the lower of the two: when that is
+	 * i_size the file is already at its final size, otherwise the
+	 * remaining dirty tail is written later and extends it the rest of
+	 * the way.
+	 *
+	 * Decide on server_eof rather than the netfs remote_i_size, which
+	 * nothing here keeps up to date: the flush above advances only
+	 * server_eof, through cifs_update_eof(), so comparing against
+	 * remote_i_size would shrink the server below data that was just
+	 * written out.
 	 */
+	spin_lock(&inode->i_lock);
 	i_size = i_size_read(inode);
-	remote_i_size = netfs_inode(inode)->remote_i_size;
-	if (end > remote_i_size && i_size > remote_i_size) {
+	server_eof = CIFS_I(inode)->server_eof;
+	spin_unlock(&inode->i_lock);
+
+	if (end > server_eof && i_size > server_eof) {
 		unsigned long long extend_to = umin(end, i_size);
 		rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
 				  cfile->fid.volatile_fid, cfile->pid, extend_to);
-		if (rc >= 0)
+		if (rc >= 0) {
+			/*
+			 * Writeback of folios outside the punched range may have
+			 * published a larger EOF while the request was in flight,
+			 * so only ever advance, as cifs_update_eof() does.
+			 */
+			spin_lock(&inode->i_lock);
+			if (extend_to > CIFS_I(inode)->server_eof)
+				CIFS_I(inode)->server_eof = extend_to;
 			netfs_inode(inode)->remote_i_size = extend_to;
+			spin_unlock(&inode->i_lock);
+		}
 	}
 
 unlock:
-- 
2.39.3


                 reply	other threads:[~2026-11-03  5:59 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20261103055923.2003159-1-joseph.qi@linux.alibaba.com \
    --to=joseph.qi@linux.alibaba.com \
    --cc=sashal@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=stfrench@microsoft.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