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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.