All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/5] smb: fix fallocate and allocation accounting
@ 2026-07-01 15:21 Huiwen He
  2026-07-01 15:21 ` [PATCH v6 1/5] smb/client: refresh allocation size after duplicate extents Huiwen He
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Huiwen He @ 2026-07-01 15:21 UTC (permalink / raw)
  To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
	senozhatsky, dhowells, metze, chenxiaosong
  Cc: linux-cifs

From: Huiwen He <hehuiwen@kylinos.cn>

Changes in v6:

- Rework patch 4 to remove the client-side statfs ENOSPC precheck. The
  server should report ENOSPC from the real allocation attempt; this fixes
  the xfstests generic/103 regression and keeps the generic/213 ENOSPC
  failure exposed with Samba "strict allocate = no".

- Add patch 5 to fix generic/213 on ksmbd by mapping SET_INFO
  -ENOSPC/-EFBIG to STATUS_DISK_FULL.

Link to v5:
https://lore.kernel.org/linux-cifs/20260630104010.571140-1-huiwen.he@linux.dev

Thanks,
Huiwen

Huiwen He (5):
  smb/client: refresh allocation size after duplicate extents
  smb/client: reduce fallocate zero buffer allocation
  smb/client: emulate small EOF-extending mode 0 fallocate ranges
  smb/client: refresh allocation after EOF-extending fallocate
  smb/server: map SET_INFO ENOSPC to disk full

 fs/smb/client/smb2ops.c   | 116 +++++++++++++++++++++++++++++++++++---
 fs/smb/client/smb2pdu.c   |  19 +++++++
 fs/smb/client/smb2proto.h |   3 +
 fs/smb/common/fscc.h      |   5 ++
 fs/smb/server/smb2pdu.c   |   2 +
 fs/smb/server/smb2pdu.h   |   4 --
 6 files changed, 136 insertions(+), 13 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v6 1/5] smb/client: refresh allocation size after duplicate extents
  2026-07-01 15:21 [PATCH v6 0/5] smb: fix fallocate and allocation accounting Huiwen He
@ 2026-07-01 15:21 ` Huiwen He
  2026-07-01 21:47   ` Steve French
  2026-07-01 15:21 ` [PATCH v6 2/5] smb/client: reduce fallocate zero buffer allocation Huiwen He
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Huiwen He @ 2026-07-01 15:21 UTC (permalink / raw)
  To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
	senozhatsky, dhowells, metze, chenxiaosong
  Cc: linux-cifs

From: Huiwen He <hehuiwen@kylinos.cn>

FSCTL_DUPLICATE_EXTENTS_TO_FILE changes the target file extents on the
server, but the client does not refresh the target AllocationSize/i_blocks.
Callers can observe or use the wrong st_blocks value immediately after the
clone, before a later attribute revalidation corrects it.

For example, create a reflinked file with a leading hole:

        xfs_io -f -c "pwrite -S 0x61 0 64k" src
        touch dst
        chmod 600 dst
        xfs_io -c "reflink src 0 1m 64k" dst
        mkswap dst
        swapon dst

The file still has a hole after mkswap:

        /mnt/scratch/dst:
          [0..7]:       allocated
          [8..2047]:    hole
          [2048..2175]: allocated

The server also reports only the allocated ranges:

        server dst size=1114112 blocks=144

but the client reported EOF-derived blocks:

        client dst size=1114112 blocks=2176

and swapon succeeded:

        swapon_result=success
        /mnt/scratch/dst 1.1M 0B -1

So EOF-derived i_blocks can let a sparse reflinked file pass the CIFS
swapfile hole check.

Fix this by querying FILE_ALL_INFORMATION on the target handle after a
successful duplicate extents request and updating i_blocks from the
returned AllocationSize. If the query fails, invalidate the cached
inode attributes so a later getattr can refresh them.

This also fixes the xfstests generic/370 regression introduced by the
i_blocks accounting change, as tested on a Samba "vfs objects = btrfs"
share.

Fixes: 99cd0a6eeb6c ("smb/client: do not account EOF extension as allocation")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 fs/smb/client/smb2ops.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 06e9322a762a..cc8e0595e504 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -2193,10 +2193,13 @@ smb2_duplicate_extents(const unsigned int xid,
 			u64 len, u64 dest_off)
 {
 	int rc;
+	int qrc;
 	unsigned int ret_data_len;
 	struct inode *inode;
+	struct smb2_file_all_info file_inf;
 	struct duplicate_extents_to_file dup_ext_buf;
 	struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
+	u64 asize;
 
 	/* server fileays advertise duplicate extent support with this flag */
 	if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) &
@@ -2232,6 +2235,19 @@ smb2_duplicate_extents(const unsigned int xid,
 	if (ret_data_len > 0)
 		cifs_dbg(FYI, "Non-zero response length in duplicate extents\n");
 
+	if (rc == 0) {
+		qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
+				      trgtfile->fid.volatile_fid, &file_inf);
+		spin_lock(&inode->i_lock);
+		if (qrc == 0) {
+			asize = le64_to_cpu(file_inf.AllocationSize);
+			inode->i_blocks = CIFS_INO_BLOCKS(asize);
+		} else {
+			CIFS_I(inode)->time = 0; /* force reval */
+		}
+		spin_unlock(&inode->i_lock);
+	}
+
 duplicate_extents_out:
 	if (rc)
 		trace_smb3_clone_err(xid, srcfile->fid.volatile_fid,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v6 2/5] smb/client: reduce fallocate zero buffer allocation
  2026-07-01 15:21 [PATCH v6 0/5] smb: fix fallocate and allocation accounting Huiwen He
  2026-07-01 15:21 ` [PATCH v6 1/5] smb/client: refresh allocation size after duplicate extents Huiwen He
@ 2026-07-01 15:21 ` Huiwen He
  2026-07-01 21:44   ` Steve French
  2026-07-01 15:21 ` [PATCH v6 3/5] smb/client: emulate small EOF-extending mode 0 fallocate ranges Huiwen He
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Huiwen He @ 2026-07-01 15:21 UTC (permalink / raw)
  To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
	senozhatsky, dhowells, metze, chenxiaosong
  Cc: linux-cifs

From: Huiwen He <hehuiwen@kylinos.cn>

The fallocate emulation allocates a 1 MiB zero-filled buffer even
though each SMB2_write request is limited to SMB2_MAX_BUFFER_SIZE,
which is 64 KiB. A high-order 1 MiB allocation is more likely to
fail on a fragmented system.

Allocate only the smaller of the requested range and SMB2_MAX_BUFFER_SIZE,
and reuse that zero-filled buffer for every write request. Also reject
a successful write that makes no progress to avoid looping indefinitely.

This reduces the contiguous allocation required by fallocate emulation
without changing the written data or range semantics.

Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 fs/smb/client/smb2ops.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index cc8e0595e504..23505ae9bd81 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3559,7 +3559,7 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
 					     char *buf)
 {
 	struct cifs_io_parms io_parms = {0};
-	int nbytes;
+	unsigned int nbytes;
 	int rc = 0;
 	struct kvec iov[2];
 
@@ -3580,9 +3580,10 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
 		rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1);
 		if (rc)
 			break;
+		if (!nbytes)
+			return -EIO;
 		if (nbytes > len)
 			return -EINVAL;
-		buf += nbytes;
 		off += nbytes;
 		len -= nbytes;
 	}
@@ -3611,7 +3612,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
 	if (rc)
 		goto out;
 
-	buf = kzalloc(1024 * 1024, GFP_KERNEL);
+	buf = kzalloc(min_t(loff_t, len, SMB2_MAX_BUFFER_SIZE), GFP_KERNEL);
 	if (buf == NULL) {
 		rc = -ENOMEM;
 		goto out;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v6 3/5] smb/client: emulate small EOF-extending mode 0 fallocate ranges
  2026-07-01 15:21 [PATCH v6 0/5] smb: fix fallocate and allocation accounting Huiwen He
  2026-07-01 15:21 ` [PATCH v6 1/5] smb/client: refresh allocation size after duplicate extents Huiwen He
  2026-07-01 15:21 ` [PATCH v6 2/5] smb/client: reduce fallocate zero buffer allocation Huiwen He
@ 2026-07-01 15:21 ` Huiwen He
  2026-07-01 15:21 ` [PATCH v6 4/5] smb/client: refresh allocation after EOF-extending fallocate Huiwen He
  2026-07-01 15:21 ` [PATCH v6 5/5] smb/server: map SET_INFO ENOSPC to disk full Huiwen He
  4 siblings, 0 replies; 9+ messages in thread
From: Huiwen He @ 2026-07-01 15:21 UTC (permalink / raw)
  To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
	senozhatsky, dhowells, metze, chenxiaosong
  Cc: linux-cifs

From: Huiwen He <hehuiwen@kylinos.cn>

When a mode 0 fallocate extends EOF from 1G to 2G + 1M, the client
currently sends SetEOF for 2G + 1M. This can make fallocate return
success without allocating the requested range, or allocate extra
space before that range.

For example, on a fresh file:

        xfs_io -f \
          -c "falloc 0 1G" \
          -c "falloc 2G 1M" \
          -c "truncate 3G" test

The second fallocate should allocate [2G, 2G + 1M), leaving [1G, 2G)
as a hole.

Before this change, the result depended on the server allocation policy.
With Samba "strict allocate = no", SetEOF could return success without
allocating [2G, 2G + 1M). With "strict allocate = yes":

	# filefrag -v test
        [0, 1G)             allocated
        [1G, 2G)            allocated unexpectedly
        [2G, 2G + 1M)       allocated

SMB cannot allocate that arbitrary range, so write zeroes to small
EOF-extending ranges instead. Limit this to 1 MiB to bound the
client-side I/O cost.

With "strict allocate = no", the requested range [2G, 2G + 1M) is
allocated by the writes. With "strict allocate = yes":

	# filefrag -v test
        [0, 1G)             allocated
        [1G, 2G)            hole
        [2G, 2G + 1M)       allocated

This fixes the small EOF-extending range case exercised by generic/213.

Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 fs/smb/client/smb2ops.c | 50 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 47 insertions(+), 3 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 23505ae9bd81..c75f55935b9b 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3682,18 +3682,22 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
 	struct cifsFileInfo *cfile = file->private_data;
 	long rc = -EOPNOTSUPP;
 	unsigned int xid;
-	loff_t new_eof;
+	loff_t old_eof, new_eof;
+	struct smb2_file_all_info file_inf;
+	u64 asize;
+	int qrc;
 
 	xid = get_xid();
 
 	inode = d_inode(cfile->dentry);
 	cifsi = CIFS_I(inode);
+	old_eof = i_size_read(inode);
 
 	trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid,
 				tcon->ses->Suid, off, len);
 	/* if file not oplocked can't be sure whether asking to extend size */
 	if (!CIFS_CACHE_READ(cifsi))
-		if (keep_size == false) {
+		if (!keep_size) {
 			trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
 				tcon->tid, tcon->ses->Suid, off, len, rc);
 			free_xid(xid);
@@ -3703,11 +3707,51 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
 	/*
 	 * Extending the file
 	 */
-	if ((keep_size == false) && i_size_read(inode) < off + len) {
+	if (!keep_size && old_eof < off + len) {
 		rc = inode_newsize_ok(inode, off + len);
 		if (rc)
 			goto out;
 
+		/*
+		 * A small range at or beyond EOF can be allocated by writing
+		 * zeroes.  For off > old_eof, this preserves the intervening
+		 * hole instead of allocating from offset 0.
+		 */
+		if (off > old_eof ||
+		    (off == old_eof && old_eof != 0 &&
+		     (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE))) {
+			if (len > 1024 * 1024) {
+				rc = -EOPNOTSUPP;
+				goto out;
+			}
+
+			rc = smb3_simple_fallocate_range(xid, tcon, cfile,
+							 off, len);
+			if (rc) {
+				spin_lock(&inode->i_lock);
+				cifsi->time = 0;
+				spin_unlock(&inode->i_lock);
+				goto out;
+			}
+
+			new_eof = off + len;
+			netfs_resize_file(&cifsi->netfs, new_eof, true);
+			cifs_setsize(inode, new_eof);
+
+			qrc = SMB2_query_info(xid, tcon,
+					      cfile->fid.persistent_fid,
+					      cfile->fid.volatile_fid, &file_inf);
+			spin_lock(&inode->i_lock);
+			if (qrc == 0) {
+				asize = le64_to_cpu(file_inf.AllocationSize);
+				inode->i_blocks = CIFS_INO_BLOCKS(asize);
+			} else {
+				cifsi->time = 0;
+			}
+			spin_unlock(&inode->i_lock);
+			goto out;
+		}
+
 		if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
 			smb2_set_sparse(xid, tcon, cfile, inode, false);
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v6 4/5] smb/client: refresh allocation after EOF-extending fallocate
  2026-07-01 15:21 [PATCH v6 0/5] smb: fix fallocate and allocation accounting Huiwen He
                   ` (2 preceding siblings ...)
  2026-07-01 15:21 ` [PATCH v6 3/5] smb/client: emulate small EOF-extending mode 0 fallocate ranges Huiwen He
@ 2026-07-01 15:21 ` Huiwen He
  2026-07-01 21:19   ` Steve French
  2026-07-01 15:21 ` [PATCH v6 5/5] smb/server: map SET_INFO ENOSPC to disk full Huiwen He
  4 siblings, 1 reply; 9+ messages in thread
From: Huiwen He @ 2026-07-01 15:21 UTC (permalink / raw)
  To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
	senozhatsky, dhowells, metze, chenxiaosong
  Cc: linux-cifs

From: Huiwen He <hehuiwen@kylinos.cn>

Before this change, xfstests generic/496 was not supported on ksmbd:

        generic/496 ... [not run] fallocated swap not supported here

ksmbd handles SetEOF as truncate, so EOF extension alone does not
allocate backing blocks. A fallocated swapfile can therefore still
look sparse to swapon.

Request allocation for EOF-extending fallocate ranges that can be
represented by FILE_ALLOCATION_INFORMATION, and refresh the allocation
state afterwards.

With this change, xfstests generic/496 and generic/701 pass on ksmbd.

However, Samba "strict allocate = no" now exposes the real generic/701
failure: the old pass came from inflated local i_blocks, not from
server allocation. generic/213 also fails in that case because an
oversized allocation request may not return ENOSPC.

Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 fs/smb/client/smb2ops.c   | 43 ++++++++++++++++++++++++++++++++++++---
 fs/smb/client/smb2pdu.c   | 19 +++++++++++++++++
 fs/smb/client/smb2proto.h |  3 +++
 fs/smb/common/fscc.h      |  5 +++++
 fs/smb/server/smb2pdu.h   |  4 ----
 5 files changed, 67 insertions(+), 7 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index c75f55935b9b..698fce6feea3 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -3756,12 +3756,49 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
 			smb2_set_sparse(xid, tcon, cfile, inode, false);
 
 		new_eof = off + len;
+
+		qrc = SMB2_query_info(xid, tcon,
+				      cfile->fid.persistent_fid,
+				      cfile->fid.volatile_fid, &file_inf);
+		if (qrc == 0)
+			asize = le64_to_cpu(file_inf.AllocationSize);
+
+		/*
+		 * FILE_ALLOCATION_INFORMATION can only describe allocation up to
+		 * new_eof. Some servers may accept it without allocating blocks,
+		 * so refresh AllocationSize before updating i_blocks.
+		 */
+		if (off == 0 || off == old_eof) {
+			if (qrc || asize < new_eof) {
+				rc = SMB2_set_allocation(xid, tcon,
+							 cfile->fid.persistent_fid,
+							 cfile->fid.volatile_fid,
+							 cfile->pid, new_eof);
+				if (rc)
+					goto out;
+			}
+		}
+
 		rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
 				  cfile->fid.volatile_fid, cfile->pid, new_eof);
-		if (rc == 0) {
-			netfs_resize_file(&cifsi->netfs, new_eof, true);
-			cifs_setsize(inode, new_eof);
+		if (rc)
+			goto out;
+
+		netfs_resize_file(&cifsi->netfs, new_eof, true);
+		cifs_setsize(inode, new_eof);
+
+		qrc = SMB2_query_info(xid, tcon,
+				      cfile->fid.persistent_fid,
+				      cfile->fid.volatile_fid, &file_inf);
+		spin_lock(&inode->i_lock);
+		if (qrc == 0) {
+			asize = le64_to_cpu(file_inf.AllocationSize);
+			if (asize >= new_eof)
+				inode->i_blocks = CIFS_INO_BLOCKS(asize);
+		} else {
+			cifsi->time = 0;
 		}
+		spin_unlock(&inode->i_lock);
 		goto out;
 	}
 
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index d058584b8f05..1374bbae627f 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -5947,6 +5947,25 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 			0, 1, &data, &size);
 }
 
+int
+SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
+		    u64 persistent_fid, u64 volatile_fid, u32 pid,
+		    loff_t allocation_size)
+{
+	struct smb2_file_alloc_info info;
+	void *data;
+	unsigned int size;
+
+	info.AllocationSize = cpu_to_le64(allocation_size);
+
+	data = &info;
+	size = sizeof(struct smb2_file_alloc_info);
+
+	return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+			pid, FILE_ALLOCATION_INFORMATION, SMB2_O_INFO_FILE,
+			0, 1, &data, &size);
+}
+
 int
 SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
 		u64 persistent_fid, u64 volatile_fid,
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 78a4e1c340f9..16a02c1eb0a1 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -204,6 +204,9 @@ void SMB2_query_directory_free(struct smb_rqst *rqst);
 int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
 		 u64 persistent_fid, u64 volatile_fid, u32 pid,
 		 loff_t new_eof);
+int SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
+			u64 persistent_fid, u64 volatile_fid, u32 pid,
+			loff_t allocation_size);
 int SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
 		       struct smb_rqst *rqst, u64 persistent_fid,
 		       u64 volatile_fid, u32 pid, u8 info_class, u8 info_type,
diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h
index 859849a42fec..941db5a95564 100644
--- a/fs/smb/common/fscc.h
+++ b/fs/smb/common/fscc.h
@@ -283,6 +283,11 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */
 	__le64 EndOfFile; /* new end of file value */
 } __packed; /* level 20 Set */
 
+/* See MS-FSCC 2.4.4 */
+struct smb2_file_alloc_info { /* encoding of request for level 19 */
+	__le64 AllocationSize;
+} __packed;
+
 /* See MS-FSCC 2.4.15 */
 typedef struct {
 	__le32 NextEntryOffset;
diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
index c2512dbcdec8..aa06c8c905f1 100644
--- a/fs/smb/server/smb2pdu.h
+++ b/fs/smb/server/smb2pdu.h
@@ -212,10 +212,6 @@ struct smb2_file_ea_info {
 	__le32 EASize;
 } __packed;
 
-struct smb2_file_alloc_info {
-	__le64 AllocationSize;
-} __packed;
-
 struct smb2_file_disposition_info {
 	__u8 DeletePending;
 } __packed;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v6 5/5] smb/server: map SET_INFO ENOSPC to disk full
  2026-07-01 15:21 [PATCH v6 0/5] smb: fix fallocate and allocation accounting Huiwen He
                   ` (3 preceding siblings ...)
  2026-07-01 15:21 ` [PATCH v6 4/5] smb/client: refresh allocation after EOF-extending fallocate Huiwen He
@ 2026-07-01 15:21 ` Huiwen He
  4 siblings, 0 replies; 9+ messages in thread
From: Huiwen He @ 2026-07-01 15:21 UTC (permalink / raw)
  To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
	senozhatsky, dhowells, metze, chenxiaosong
  Cc: linux-cifs

From: Huiwen He <hehuiwen@kylinos.cn>

FILE_ALLOCATION_INFORMATION can call vfs_fallocate(). If the allocation
cannot be satisfied, vfs_fallocate() returns -ENOSPC.

smb2_set_info() did not map -ENOSPC, so ksmbd returned a generic SMB error
and the client reported EIO instead of ENOSPC. This makes the ENOSPC step
in xfstests generic/213 fail.

Map -ENOSPC and -EFBIG to STATUS_DISK_FULL in the SET_INFO error path.

Tested with xfstests generic/213 on ksmbd.

Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 fs/smb/server/smb2pdu.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 5859fa68bb84..8bd8f348d0e3 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -7077,6 +7077,8 @@ int smb2_set_info(struct ksmbd_work *work)
 		rsp->hdr.Status = STATUS_INVALID_PARAMETER;
 	else if (rc == -EMSGSIZE)
 		rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH;
+	else if (rc == -ENOSPC || rc == -EFBIG)
+		rsp->hdr.Status = STATUS_DISK_FULL;
 	else if (rc == -ESHARE)
 		rsp->hdr.Status = STATUS_SHARING_VIOLATION;
 	else if (rc == -ENOENT)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 4/5] smb/client: refresh allocation after EOF-extending fallocate
  2026-07-01 15:21 ` [PATCH v6 4/5] smb/client: refresh allocation after EOF-extending fallocate Huiwen He
@ 2026-07-01 21:19   ` Steve French
  0 siblings, 0 replies; 9+ messages in thread
From: Steve French @ 2026-07-01 21:19 UTC (permalink / raw)
  To: Huiwen He
  Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, dhowells,
	metze, chenxiaosong, linux-cifs

With this patch I do see a difference in generic/103 which with the
patch returns

generic/103  0s ... - output mismatch (see
/home/smfrench/xfstests-dev/results//sambamfs/generic/103.out.bad)
    --- tests/generic/103.out 2025-08-22 09:54:32.266754366 -0500
    +++ /home/smfrench/xfstests-dev/results//sambamfs/generic/103.out.bad
2026-07-01 16:13:02.220130644 -0500
    @@ -1,2 +1,3 @@
     QA output created by 103
    +fallocate: No space left on device
     Silence is golden.

but without that patch works.  Also is puzzling why the error changes
for generic/539 with the four fallocate patches from:

generic/539  0s ... [failed, exit status 1]- output mismatch (see
/home/smfrench/xfstests-dev/results//sambamfs/generic/539.out.bad)
    --- tests/generic/539.out 2025-08-22 09:54:32.329794445 -0500
    +++ /home/smfrench/xfstests-dev/results//sambamfs/generic/539.out.bad
2026-06-30 20:27:26.739245628 -0500
    @@ -1,2 +1,4 @@
     QA output created by 539
     Silence is golden
    +seek sanity check failed!
    +(see /home/smfrench/xfstests-dev/results//sambamfs/generic/539.full
for details)
    ...

to
   generic/539  0s ... [not run] File system does not support
llseek(2) SEEK_DATA/HOLE

Samba supports SEEK_DATA/SEEK_HOLE right?

On Wed, Jul 1, 2026 at 10:22 AM Huiwen He <huiwen.he@linux.dev> wrote:
>
> From: Huiwen He <hehuiwen@kylinos.cn>
>
> Before this change, xfstests generic/496 was not supported on ksmbd:
>
>         generic/496 ... [not run] fallocated swap not supported here
>
> ksmbd handles SetEOF as truncate, so EOF extension alone does not
> allocate backing blocks. A fallocated swapfile can therefore still
> look sparse to swapon.
>
> Request allocation for EOF-extending fallocate ranges that can be
> represented by FILE_ALLOCATION_INFORMATION, and refresh the allocation
> state afterwards.
>
> With this change, xfstests generic/496 and generic/701 pass on ksmbd.
>
> However, Samba "strict allocate = no" now exposes the real generic/701
> failure: the old pass came from inflated local i_blocks, not from
> server allocation. generic/213 also fails in that case because an
> oversized allocation request may not return ENOSPC.
>
> Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
> ---
>  fs/smb/client/smb2ops.c   | 43 ++++++++++++++++++++++++++++++++++++---
>  fs/smb/client/smb2pdu.c   | 19 +++++++++++++++++
>  fs/smb/client/smb2proto.h |  3 +++
>  fs/smb/common/fscc.h      |  5 +++++
>  fs/smb/server/smb2pdu.h   |  4 ----
>  5 files changed, 67 insertions(+), 7 deletions(-)
>
> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
> index c75f55935b9b..698fce6feea3 100644
> --- a/fs/smb/client/smb2ops.c
> +++ b/fs/smb/client/smb2ops.c
> @@ -3756,12 +3756,49 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
>                         smb2_set_sparse(xid, tcon, cfile, inode, false);
>
>                 new_eof = off + len;
> +
> +               qrc = SMB2_query_info(xid, tcon,
> +                                     cfile->fid.persistent_fid,
> +                                     cfile->fid.volatile_fid, &file_inf);
> +               if (qrc == 0)
> +                       asize = le64_to_cpu(file_inf.AllocationSize);
> +
> +               /*
> +                * FILE_ALLOCATION_INFORMATION can only describe allocation up to
> +                * new_eof. Some servers may accept it without allocating blocks,
> +                * so refresh AllocationSize before updating i_blocks.
> +                */
> +               if (off == 0 || off == old_eof) {
> +                       if (qrc || asize < new_eof) {
> +                               rc = SMB2_set_allocation(xid, tcon,
> +                                                        cfile->fid.persistent_fid,
> +                                                        cfile->fid.volatile_fid,
> +                                                        cfile->pid, new_eof);
> +                               if (rc)
> +                                       goto out;
> +                       }
> +               }
> +
>                 rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
>                                   cfile->fid.volatile_fid, cfile->pid, new_eof);
> -               if (rc == 0) {
> -                       netfs_resize_file(&cifsi->netfs, new_eof, true);
> -                       cifs_setsize(inode, new_eof);
> +               if (rc)
> +                       goto out;
> +
> +               netfs_resize_file(&cifsi->netfs, new_eof, true);
> +               cifs_setsize(inode, new_eof);
> +
> +               qrc = SMB2_query_info(xid, tcon,
> +                                     cfile->fid.persistent_fid,
> +                                     cfile->fid.volatile_fid, &file_inf);
> +               spin_lock(&inode->i_lock);
> +               if (qrc == 0) {
> +                       asize = le64_to_cpu(file_inf.AllocationSize);
> +                       if (asize >= new_eof)
> +                               inode->i_blocks = CIFS_INO_BLOCKS(asize);
> +               } else {
> +                       cifsi->time = 0;
>                 }
> +               spin_unlock(&inode->i_lock);
>                 goto out;
>         }
>
> diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
> index d058584b8f05..1374bbae627f 100644
> --- a/fs/smb/client/smb2pdu.c
> +++ b/fs/smb/client/smb2pdu.c
> @@ -5947,6 +5947,25 @@ SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
>                         0, 1, &data, &size);
>  }
>
> +int
> +SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
> +                   u64 persistent_fid, u64 volatile_fid, u32 pid,
> +                   loff_t allocation_size)
> +{
> +       struct smb2_file_alloc_info info;
> +       void *data;
> +       unsigned int size;
> +
> +       info.AllocationSize = cpu_to_le64(allocation_size);
> +
> +       data = &info;
> +       size = sizeof(struct smb2_file_alloc_info);
> +
> +       return send_set_info(xid, tcon, persistent_fid, volatile_fid,
> +                       pid, FILE_ALLOCATION_INFORMATION, SMB2_O_INFO_FILE,
> +                       0, 1, &data, &size);
> +}
> +
>  int
>  SMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
>                 u64 persistent_fid, u64 volatile_fid,
> diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
> index 78a4e1c340f9..16a02c1eb0a1 100644
> --- a/fs/smb/client/smb2proto.h
> +++ b/fs/smb/client/smb2proto.h
> @@ -204,6 +204,9 @@ void SMB2_query_directory_free(struct smb_rqst *rqst);
>  int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
>                  u64 persistent_fid, u64 volatile_fid, u32 pid,
>                  loff_t new_eof);
> +int SMB2_set_allocation(const unsigned int xid, struct cifs_tcon *tcon,
> +                       u64 persistent_fid, u64 volatile_fid, u32 pid,
> +                       loff_t allocation_size);
>  int SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
>                        struct smb_rqst *rqst, u64 persistent_fid,
>                        u64 volatile_fid, u32 pid, u8 info_class, u8 info_type,
> diff --git a/fs/smb/common/fscc.h b/fs/smb/common/fscc.h
> index 859849a42fec..941db5a95564 100644
> --- a/fs/smb/common/fscc.h
> +++ b/fs/smb/common/fscc.h
> @@ -283,6 +283,11 @@ struct smb2_file_eof_info { /* encoding of request for level 10 */
>         __le64 EndOfFile; /* new end of file value */
>  } __packed; /* level 20 Set */
>
> +/* See MS-FSCC 2.4.4 */
> +struct smb2_file_alloc_info { /* encoding of request for level 19 */
> +       __le64 AllocationSize;
> +} __packed;
> +
>  /* See MS-FSCC 2.4.15 */
>  typedef struct {
>         __le32 NextEntryOffset;
> diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h
> index c2512dbcdec8..aa06c8c905f1 100644
> --- a/fs/smb/server/smb2pdu.h
> +++ b/fs/smb/server/smb2pdu.h
> @@ -212,10 +212,6 @@ struct smb2_file_ea_info {
>         __le32 EASize;
>  } __packed;
>
> -struct smb2_file_alloc_info {
> -       __le64 AllocationSize;
> -} __packed;
> -
>  struct smb2_file_disposition_info {
>         __u8 DeletePending;
>  } __packed;
> --
> 2.43.0
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 2/5] smb/client: reduce fallocate zero buffer allocation
  2026-07-01 15:21 ` [PATCH v6 2/5] smb/client: reduce fallocate zero buffer allocation Huiwen He
@ 2026-07-01 21:44   ` Steve French
  0 siblings, 0 replies; 9+ messages in thread
From: Steve French @ 2026-07-01 21:44 UTC (permalink / raw)
  To: Huiwen He
  Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, dhowells,
	metze, chenxiaosong, linux-cifs

Won't this hurt performance since for most servers the default max
write size is 4MB (e.g. Samba or Windows) or 1MB?  I did a quick
experiment with
"dd" and I see the 4MB writes over the wire not 64K

On Wed, Jul 1, 2026 at 10:22 AM Huiwen He <huiwen.he@linux.dev> wrote:
>
> From: Huiwen He <hehuiwen@kylinos.cn>
>
> The fallocate emulation allocates a 1 MiB zero-filled buffer even
> though each SMB2_write request is limited to SMB2_MAX_BUFFER_SIZE,
> which is 64 KiB. A high-order 1 MiB allocation is more likely to
> fail on a fragmented system.
>
> Allocate only the smaller of the requested range and SMB2_MAX_BUFFER_SIZE,
> and reuse that zero-filled buffer for every write request. Also reject
> a successful write that makes no progress to avoid looping indefinitely.
>
> This reduces the contiguous allocation required by fallocate emulation
> without changing the written data or range semantics.
>
> Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
> ---
>  fs/smb/client/smb2ops.c | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
> index cc8e0595e504..23505ae9bd81 100644
> --- a/fs/smb/client/smb2ops.c
> +++ b/fs/smb/client/smb2ops.c
> @@ -3559,7 +3559,7 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
>                                              char *buf)
>  {
>         struct cifs_io_parms io_parms = {0};
> -       int nbytes;
> +       unsigned int nbytes;
>         int rc = 0;
>         struct kvec iov[2];
>
> @@ -3580,9 +3580,10 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
>                 rc = SMB2_write(xid, &io_parms, &nbytes, iov, 1);
>                 if (rc)
>                         break;
> +               if (!nbytes)
> +                       return -EIO;
>                 if (nbytes > len)
>                         return -EINVAL;
> -               buf += nbytes;
>                 off += nbytes;
>                 len -= nbytes;
>         }
> @@ -3611,7 +3612,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
>         if (rc)
>                 goto out;
>
> -       buf = kzalloc(1024 * 1024, GFP_KERNEL);
> +       buf = kzalloc(min_t(loff_t, len, SMB2_MAX_BUFFER_SIZE), GFP_KERNEL);
>         if (buf == NULL) {
>                 rc = -ENOMEM;
>                 goto out;
> --
> 2.43.0
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 1/5] smb/client: refresh allocation size after duplicate extents
  2026-07-01 15:21 ` [PATCH v6 1/5] smb/client: refresh allocation size after duplicate extents Huiwen He
@ 2026-07-01 21:47   ` Steve French
  0 siblings, 0 replies; 9+ messages in thread
From: Steve French @ 2026-07-01 21:47 UTC (permalink / raw)
  To: Huiwen He
  Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, dhowells,
	metze, chenxiaosong, linux-cifs, Sergey Senozhatsky

This does lead to an extra roundtrip (common operations like cp will
use duplicate extents in some Samba and Windows server
configurations).  Any worries about performance hit?

On Wed, Jul 1, 2026 at 10:22 AM Huiwen He <huiwen.he@linux.dev> wrote:
>
> From: Huiwen He <hehuiwen@kylinos.cn>
>
> FSCTL_DUPLICATE_EXTENTS_TO_FILE changes the target file extents on the
> server, but the client does not refresh the target AllocationSize/i_blocks.
> Callers can observe or use the wrong st_blocks value immediately after the
> clone, before a later attribute revalidation corrects it.
>
> For example, create a reflinked file with a leading hole:
>
>         xfs_io -f -c "pwrite -S 0x61 0 64k" src
>         touch dst
>         chmod 600 dst
>         xfs_io -c "reflink src 0 1m 64k" dst
>         mkswap dst
>         swapon dst
>
> The file still has a hole after mkswap:
>
>         /mnt/scratch/dst:
>           [0..7]:       allocated
>           [8..2047]:    hole
>           [2048..2175]: allocated
>
> The server also reports only the allocated ranges:
>
>         server dst size=1114112 blocks=144
>
> but the client reported EOF-derived blocks:
>
>         client dst size=1114112 blocks=2176
>
> and swapon succeeded:
>
>         swapon_result=success
>         /mnt/scratch/dst 1.1M 0B -1
>
> So EOF-derived i_blocks can let a sparse reflinked file pass the CIFS
> swapfile hole check.
>
> Fix this by querying FILE_ALL_INFORMATION on the target handle after a
> successful duplicate extents request and updating i_blocks from the
> returned AllocationSize. If the query fails, invalidate the cached
> inode attributes so a later getattr can refresh them.
>
> This also fixes the xfstests generic/370 regression introduced by the
> i_blocks accounting change, as tested on a Samba "vfs objects = btrfs"
> share.
>
> Fixes: 99cd0a6eeb6c ("smb/client: do not account EOF extension as allocation")
> Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
> ---
>  fs/smb/client/smb2ops.c | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
>
> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
> index 06e9322a762a..cc8e0595e504 100644
> --- a/fs/smb/client/smb2ops.c
> +++ b/fs/smb/client/smb2ops.c
> @@ -2193,10 +2193,13 @@ smb2_duplicate_extents(const unsigned int xid,
>                         u64 len, u64 dest_off)
>  {
>         int rc;
> +       int qrc;
>         unsigned int ret_data_len;
>         struct inode *inode;
> +       struct smb2_file_all_info file_inf;
>         struct duplicate_extents_to_file dup_ext_buf;
>         struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
> +       u64 asize;
>
>         /* server fileays advertise duplicate extent support with this flag */
>         if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) &
> @@ -2232,6 +2235,19 @@ smb2_duplicate_extents(const unsigned int xid,
>         if (ret_data_len > 0)
>                 cifs_dbg(FYI, "Non-zero response length in duplicate extents\n");
>
> +       if (rc == 0) {
> +               qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
> +                                     trgtfile->fid.volatile_fid, &file_inf);
> +               spin_lock(&inode->i_lock);
> +               if (qrc == 0) {
> +                       asize = le64_to_cpu(file_inf.AllocationSize);
> +                       inode->i_blocks = CIFS_INO_BLOCKS(asize);
> +               } else {
> +                       CIFS_I(inode)->time = 0; /* force reval */
> +               }
> +               spin_unlock(&inode->i_lock);
> +       }
> +
>  duplicate_extents_out:
>         if (rc)
>                 trace_smb3_clone_err(xid, srcfile->fid.volatile_fid,
> --
> 2.43.0
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-07-01 21:47 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 15:21 [PATCH v6 0/5] smb: fix fallocate and allocation accounting Huiwen He
2026-07-01 15:21 ` [PATCH v6 1/5] smb/client: refresh allocation size after duplicate extents Huiwen He
2026-07-01 21:47   ` Steve French
2026-07-01 15:21 ` [PATCH v6 2/5] smb/client: reduce fallocate zero buffer allocation Huiwen He
2026-07-01 21:44   ` Steve French
2026-07-01 15:21 ` [PATCH v6 3/5] smb/client: emulate small EOF-extending mode 0 fallocate ranges Huiwen He
2026-07-01 15:21 ` [PATCH v6 4/5] smb/client: refresh allocation after EOF-extending fallocate Huiwen He
2026-07-01 21:19   ` Steve French
2026-07-01 15:21 ` [PATCH v6 5/5] smb/server: map SET_INFO ENOSPC to disk full Huiwen He

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.