* [PATCH v2 0/3] smb: client: fix i_blocks accounting for swapfile xfstests
@ 2026-06-05 16:35 Huiwen He
2026-06-05 16:35 ` [PATCH v2 1/3] smb/client: update i_blocks after contiguous writes Huiwen He
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Huiwen He @ 2026-06-05 16:35 UTC (permalink / raw)
To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
senozhatsky, dhowells, metze, chenxiaosong
Cc: linux-cifs
From: Huiwen He <hehuiwen@kylinos.cn>
This series fixes CIFS swapfile xfstests failures caused by incorrect local
i_blocks accounting while cached inode attributes can be used.
The patches cover three cases:
1. Update i_blocks after contiguous writes, so fully written swapfiles do
not look sparse.
2. Do not grow i_blocks on EOF extension, since extending EOF does not
prove that the range was allocated.
3. After EOF-extending fallocate, refresh i_blocks from the
server-reported AllocationSize.
In my setup, the write-created swapfile tests pass with the cifsacl mount
option:
generic/472 generic/494 generic/497
generic/569 generic/636 generic/643
generic/496 also passes with this series when the server really allocates the
fallocated range, for example with Samba:
[scratch_share]
strict allocate = yes
generic/495 depends on the server-reported allocation unit matching the page
size. In my Samba setup this requires:
[global]
block size = 4096
---
Changes since v1:
- Patch 1 was updated to cover more CIFS write paths and to use common
helpers for local i_blocks accounting.
- Patches 2 and 3 are unchanged.
Link to v1:
https://lore.kernel.org/linux-cifs/20260604150349.101716-1-huiwen.he@linux.dev/
---
Thanks,
Huiwen He
Huiwen He (3):
smb/client: update i_blocks after contiguous writes
smb/client: do not account EOF extension as allocation
smb/client: refresh allocation size after fallocate
fs/smb/client/cifsfs.c | 4 ++--
fs/smb/client/cifsfs.h | 1 +
fs/smb/client/cifsglob.h | 9 ++++---
fs/smb/client/file.c | 52 ++++++++++++++++++++++++++++++++++++++++
fs/smb/client/inode.c | 13 +++++++---
fs/smb/client/smb2ops.c | 15 ++++++++++++
6 files changed, 86 insertions(+), 8 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v2 1/3] smb/client: update i_blocks after contiguous writes 2026-06-05 16:35 [PATCH v2 0/3] smb: client: fix i_blocks accounting for swapfile xfstests Huiwen He @ 2026-06-05 16:35 ` Huiwen He 2026-06-05 16:35 ` [PATCH v2 2/3] smb/client: do not account EOF extension as allocation Huiwen He 2026-06-05 16:35 ` [PATCH v2 3/3] smb/client: refresh allocation size after fallocate Huiwen He 2 siblings, 0 replies; 8+ messages in thread From: Huiwen He @ 2026-06-05 16:35 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 lease allows CIFS to use cached inode attributes, getattr may return the locally cached attributes instead of revalidating them from the server. After local writes extend a file, the write path updates the file size, but i_blocks can remain based on the old allocation size. For example, while the file is still open after two contiguous writes, the local block count can remain smaller than the written range: after first write: st_size = 4096, st_blocks = 7 after second write: st_size = 12288, st_blocks = 21 after close: st_size = 12288, st_blocks = 24 This can make a fully written file look sparse: i_blocks * 512 < i_size and can cause swap activation to reject a valid write-created swapfile as having holes. This results in xfstests skipping swap-related tests on CIFS mounts: generic/472 [not run] swapfiles are not supported generic/494 [not run] swapfiles are not supported generic/497 [not run] swapfiles are not supported generic/569 [not run] swapfiles are not supported generic/636 [not run] swapfiles are not supported generic/643 [not run] swapfiles are not supported Update the local i_blocks estimate after successful writes, but only when the write starts at or before the currently known allocated range. This lets sequential writes grow i_blocks while avoiding treating write-past-EOF holes as allocated. Skip the local estimate for files that are already marked sparse, since their allocation needs to come from the server rather than from a contiguous-write estimate. Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> --- fs/smb/client/cifsfs.c | 4 ++-- fs/smb/client/cifsfs.h | 1 + fs/smb/client/cifsglob.h | 9 ++++--- fs/smb/client/file.c | 52 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index ce23924f01b3..6b97f7a91235 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -1615,7 +1615,7 @@ const struct file_operations cifs_file_strict_ops = { const struct file_operations cifs_file_direct_ops = { .read_iter = netfs_unbuffered_read_iter, - .write_iter = netfs_file_write_iter, + .write_iter = cifs_direct_write_iter, .open = cifs_open, .release = cifs_close, .lock = cifs_lock, @@ -1671,7 +1671,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = { const struct file_operations cifs_file_direct_nobrl_ops = { .read_iter = netfs_unbuffered_read_iter, - .write_iter = netfs_file_write_iter, + .write_iter = cifs_direct_write_iter, .open = cifs_open, .release = cifs_close, .fsync = cifs_fsync, diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index c455b15f2778..c35074ce5aad 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -104,6 +104,7 @@ int cifs_closedir(struct inode *inode, struct file *file); ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from); ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from); +ssize_t cifs_direct_write_iter(struct kiocb *iocb, struct iov_iter *from); ssize_t cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter); int cifs_flock(struct file *file, int cmd, struct file_lock *fl); int cifs_lock(struct file *file, int cmd, struct file_lock *flock); diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 82e0adc1dabd..943b7cd2c096 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -2387,9 +2387,12 @@ static inline int cifs_open_create_options(unsigned int oflags, int opts) } /* - * The number of blocks is not related to (i_size / i_blksize), but instead - * 512 byte (2**9) size is required for calculating num blocks. + * inode->i_blocks is counted in 512-byte units, independent of + * inode->i_blksize. */ -#define CIFS_INO_BLOCKS(size) DIV_ROUND_UP_ULL((u64)(size), 512) +#define CIFS_INO_BLOCK_SIZE 512ULL +#define CIFS_INO_BLOCKS(size) \ + DIV_ROUND_UP_ULL((u64)(size), CIFS_INO_BLOCK_SIZE) +#define CIFS_INO_BYTES(blocks) ((u64)(blocks) * CIFS_INO_BLOCK_SIZE) #endif /* _CIFS_GLOB_H */ diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index b60344125f27..58430ba51b10 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -2514,6 +2514,42 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) return rc; } +static void cifs_update_i_blocks_for_write(struct inode *inode, loff_t start, + loff_t end) +{ + struct cifsInodeInfo *cinode = CIFS_I(inode); + u64 allocated_end = CIFS_INO_BYTES(inode->i_blocks); + u64 blocks; + + if (cinode->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) + return; + + /* + * Grow the local estimate only across the currently known allocated + * prefix. A write beyond that may leave a hole. + */ + if ((u64)start > allocated_end) + return; + + blocks = CIFS_INO_BLOCKS(end); + if ((u64)inode->i_blocks < blocks) + inode->i_blocks = blocks; +} + +static void cifs_update_i_blocks_after_write(struct kiocb *iocb, + ssize_t written) +{ + struct inode *inode = file_inode(iocb->ki_filp); + loff_t end = iocb->ki_pos; + + if (written <= 0) + return; + + spin_lock(&inode->i_lock); + cifs_update_i_blocks_for_write(inode, end - written, end); + spin_unlock(&inode->i_lock); +} + void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t result) { struct netfs_io_request *wreq = wdata->rreq; @@ -2532,6 +2568,8 @@ void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t netfs_write_zero_point(inode, wrend); if (wrend > ictx->_remote_i_size) netfs_resize_file(ictx, wrend, true); + cifs_update_i_blocks_for_write(inode, wdata->subreq.start, + wrend); spin_unlock(&inode->i_lock); } @@ -2920,6 +2958,7 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) } rc = netfs_buffered_write_iter_locked(iocb, from, NULL); + cifs_update_i_blocks_after_write(iocb, rc); out: up_read(&cinode->lock_sem); @@ -2949,6 +2988,7 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) && ((cifs_sb_flags(cifs_sb) & CIFS_MOUNT_NOPOSIXBRL) == 0)) { written = netfs_file_write_iter(iocb, from); + cifs_update_i_blocks_after_write(iocb, written); goto out; } written = cifs_writev(iocb, from); @@ -2961,6 +3001,7 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) * these pages but not on the region from pos to ppos+len-1. */ written = netfs_file_write_iter(iocb, from); + cifs_update_i_blocks_after_write(iocb, written); if (CIFS_CACHE_READ(cinode)) { /* * We have read level caching and we have just sent a write @@ -2979,6 +3020,15 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from) return written; } +ssize_t cifs_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + ssize_t written; + + written = netfs_file_write_iter(iocb, from); + cifs_update_i_blocks_after_write(iocb, written); + return written; +} + ssize_t cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter) { ssize_t rc; @@ -3003,6 +3053,7 @@ ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (iocb->ki_filp->f_flags & O_DIRECT) { written = netfs_unbuffered_write_iter(iocb, from); + cifs_update_i_blocks_after_write(iocb, written); if (written > 0 && CIFS_CACHE_READ(cinode)) { cifs_zap_mapping(inode); cifs_dbg(FYI, @@ -3018,6 +3069,7 @@ ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) return written; written = netfs_file_write_iter(iocb, from); + cifs_update_i_blocks_after_write(iocb, written); if (!CIFS_CACHE_WRITE(CIFS_I(inode))) { rc = filemap_fdatawrite(inode->i_mapping); -- 2.43.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2 2/3] smb/client: do not account EOF extension as allocation 2026-06-05 16:35 [PATCH v2 0/3] smb: client: fix i_blocks accounting for swapfile xfstests Huiwen He 2026-06-05 16:35 ` [PATCH v2 1/3] smb/client: update i_blocks after contiguous writes Huiwen He @ 2026-06-05 16:35 ` Huiwen He 2026-06-07 15:33 ` Steve French 2026-06-05 16:35 ` [PATCH v2 3/3] smb/client: refresh allocation size after fallocate Huiwen He 2 siblings, 1 reply; 8+ messages in thread From: Huiwen He @ 2026-06-05 16:35 UTC (permalink / raw) To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, senozhatsky, dhowells, metze, chenxiaosong Cc: linux-cifs From: Huiwen He <hehuiwen@kylinos.cn> cifs_setsize() updates the local inode size after SetEOF succeeds. It also used the new EOF as a local i_blocks estimate, but extending EOF does not prove that the intervening range was allocated. For example, after writing 1 MiB and then extending EOF to 10 MiB, the client can report the file as fully allocated even though the server still reports a much smaller AllocationSize: $ dd if=/dev/zero of=test bs=1M count=1 $ truncate -s 10M test && stat -c 'size=%s blocks=%b' test $ stat --cached=never -c 'size=%s blocks=%b' test client stat: size=10485760 blocks=20480 server stat: size=10485760 blocks=2056 client stat(nocache): size=10485760 blocks=2056 A later attribute revalidation may correct i_blocks, but callers such as xfstests generic/495 invoke swapon immediately after truncate. The swapfile hole check can therefore observe the inflated local i_blocks value and accept a sparse file. Do not grow i_blocks from cifs_setsize() on EOF extension. Only clamp it on shrink; allocation growth must come from write completion or from server-reported AllocationSize. With this change, EOF extension no longer makes a sparse file appear fully allocated before the next attribute revalidation, and xfstests generic/495 correctly rejects the sparse swapfile. Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> --- fs/smb/client/inode.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 826d36ed13ec..e0aa71b87d27 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3038,13 +3038,20 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, void cifs_setsize(struct inode *inode, loff_t offset) { + loff_t old_size; + u64 blocks = CIFS_INO_BLOCKS(offset); + spin_lock(&inode->i_lock); + old_size = i_size_read(inode); i_size_write(inode, offset); + /* - * Until we can query the server for actual allocation size, - * this is best estimate we have for blocks allocated for a file. + * Extending EOF does not allocate the intervening range. Only clamp + * i_blocks on shrink; allocation growth comes from writes or from the + * server-reported AllocationSize. */ - inode->i_blocks = CIFS_INO_BLOCKS(offset); + if (offset < old_size && (u64)inode->i_blocks > blocks) + inode->i_blocks = blocks; spin_unlock(&inode->i_lock); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); truncate_pagecache(inode, offset); -- 2.43.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2 2/3] smb/client: do not account EOF extension as allocation 2026-06-05 16:35 ` [PATCH v2 2/3] smb/client: do not account EOF extension as allocation Huiwen He @ 2026-06-07 15:33 ` Steve French 0 siblings, 0 replies; 8+ messages in thread From: Steve French @ 2026-06-07 15:33 UTC (permalink / raw) To: Huiwen He Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, senozhatsky, dhowells, metze, chenxiaosong, linux-cifs This patch regresses xfstests generic/568 and generic/701 can you update it to address that regression? generic/568 0s ... - output mismatch (see /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad) --- tests/generic/568.out 2025-08-22 09:54:32.333796989 -0500 +++ /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad 2026-06-07 10:20:34.002140048 -0500 @@ -1,4 +1,4 @@ QA output created by 568 wrote 2/2 bytes at offset block_size - 1 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -OK: File did not grow. +ERROR: File grew from 0 B to 1536 B when writing to the fallocated range. ... (Run 'diff -u /home/smfrench/xfstests-dev/tests/generic/568.out /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad' to see the entire diff) generic/701 1s ... - output mismatch (see /home/smfrench/xfstests-dev/results//sambamfs/generic/701.out.bad) --- tests/generic/701.out 2025-08-22 09:54:32.350807804 -0500 +++ /home/smfrench/xfstests-dev/results//sambamfs/generic/701.out.bad 2026-06-07 10:20:34.573010303 -0500 @@ -1,2 +1,3 @@ QA output created by 701 -Number of allocated blocks after truncate is in range +Number of allocated blocks after truncate has value of 0 +Number of allocated blocks after truncate is NOT in range 8304721.92 .. 8472494.08 ... (Run 'diff -u /home/smfrench/xfstests-dev/tests/generic/701.out /home/smfrench/xfstests-dev/results//sambamfs/generic/701.out.bad' to see the entire diff) On Fri, Jun 5, 2026 at 11:36 AM Huiwen He <huiwen.he@linux.dev> wrote: > > From: Huiwen He <hehuiwen@kylinos.cn> > > cifs_setsize() updates the local inode size after SetEOF succeeds. It also > used the new EOF as a local i_blocks estimate, but extending EOF does not > prove that the intervening range was allocated. > > For example, after writing 1 MiB and then extending EOF to 10 MiB, the > client can report the file as fully allocated even though the server still > reports a much smaller AllocationSize: > > $ dd if=/dev/zero of=test bs=1M count=1 > $ truncate -s 10M test && stat -c 'size=%s blocks=%b' test > $ stat --cached=never -c 'size=%s blocks=%b' test > > client stat: size=10485760 blocks=20480 > server stat: size=10485760 blocks=2056 > client stat(nocache): size=10485760 blocks=2056 > > A later attribute revalidation may correct i_blocks, but callers such as > xfstests generic/495 invoke swapon immediately after truncate. The swapfile > hole check can therefore observe the inflated local i_blocks value and > accept a sparse file. > > Do not grow i_blocks from cifs_setsize() on EOF extension. Only clamp it > on shrink; allocation growth must come from write completion or from > server-reported AllocationSize. > > With this change, EOF extension no longer makes a sparse file appear > fully allocated before the next attribute revalidation, and xfstests > generic/495 correctly rejects the sparse swapfile. > > Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> > Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> > --- > fs/smb/client/inode.c | 13 ++++++++++--- > 1 file changed, 10 insertions(+), 3 deletions(-) > > diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c > index 826d36ed13ec..e0aa71b87d27 100644 > --- a/fs/smb/client/inode.c > +++ b/fs/smb/client/inode.c > @@ -3038,13 +3038,20 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, > > void cifs_setsize(struct inode *inode, loff_t offset) > { > + loff_t old_size; > + u64 blocks = CIFS_INO_BLOCKS(offset); > + > spin_lock(&inode->i_lock); > + old_size = i_size_read(inode); > i_size_write(inode, offset); > + > /* > - * Until we can query the server for actual allocation size, > - * this is best estimate we have for blocks allocated for a file. > + * Extending EOF does not allocate the intervening range. Only clamp > + * i_blocks on shrink; allocation growth comes from writes or from the > + * server-reported AllocationSize. > */ > - inode->i_blocks = CIFS_INO_BLOCKS(offset); > + if (offset < old_size && (u64)inode->i_blocks > blocks) > + inode->i_blocks = blocks; > spin_unlock(&inode->i_lock); > inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); > truncate_pagecache(inode, offset); > -- > 2.43.0 > -- Thanks, Steve ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2 3/3] smb/client: refresh allocation size after fallocate 2026-06-05 16:35 [PATCH v2 0/3] smb: client: fix i_blocks accounting for swapfile xfstests Huiwen He 2026-06-05 16:35 ` [PATCH v2 1/3] smb/client: update i_blocks after contiguous writes Huiwen He 2026-06-05 16:35 ` [PATCH v2 2/3] smb/client: do not account EOF extension as allocation Huiwen He @ 2026-06-05 16:35 ` Huiwen He 2026-06-07 15:35 ` Steve French 2 siblings, 1 reply; 8+ messages in thread From: Huiwen He @ 2026-06-05 16:35 UTC (permalink / raw) To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, senozhatsky, dhowells, metze, chenxiaosong Cc: linux-cifs From: Huiwen He <hehuiwen@kylinos.cn> SMB3 fallocate extends EOF using FILE_END_OF_FILE_INFORMATION, but the server may also update the file's AllocationSize. If the client keeps the old cached i_blocks value after fallocate, the swapfile hole check can still see: i_blocks * 512 < i_size and reject the file as sparse. This shows up in xfstests generic/496 as: generic/496 [not run] fallocated swap not supported here After a successful EOF-extending fallocate, query FILE_ALL_INFORMATION on the open handle and update i_blocks from the returned AllocationSize. If the query fails, leave the fallocate result unchanged and force a later attribute revalidation by setting cifsi->time to zero. With this client-side refresh, and with a server that really allocates the fallocated range, for example Samba configured with: [scratch_share] strict allocate = yes generic/496 can pass the swapfile hole check. Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> --- fs/smb/client/smb2ops.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index d4875f9532b4..89230141b5dd 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -3698,8 +3698,23 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, cfile->pid, new_eof); if (rc == 0) { + struct smb2_file_all_info file_inf; + u64 asize; + int qrc; + 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; } -- 2.43.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/3] smb/client: refresh allocation size after fallocate 2026-06-05 16:35 ` [PATCH v2 3/3] smb/client: refresh allocation size after fallocate Huiwen He @ 2026-06-07 15:35 ` Steve French 2026-06-07 16:20 ` hehuiwen 2026-06-08 15:28 ` hehuiwen 0 siblings, 2 replies; 8+ messages in thread From: Steve French @ 2026-06-07 15:35 UTC (permalink / raw) To: Huiwen He Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, senozhatsky, dhowells, metze, chenxiaosong, linux-cifs This regresses xfstests generic/568. generic/568 1s ... - output mismatch (see /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad) --- tests/generic/568.out 2025-08-22 09:54:32.333796989 -0500 +++ /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad 2026-06-07 10:32:28.896135808 -0500 @@ -1,4 +1,4 @@ QA output created by 568 wrote 2/2 bytes at offset block_size - 1 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -OK: File did not grow. +ERROR: File grew from 512 B to 1536 B when writing to the fallocated range. ... (Run 'diff -u /home/smfrench/xfstests-dev/tests/generic/568.out /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad' to see the entire diff) On Fri, Jun 5, 2026 at 11:36 AM Huiwen He <huiwen.he@linux.dev> wrote: > > From: Huiwen He <hehuiwen@kylinos.cn> > > SMB3 fallocate extends EOF using FILE_END_OF_FILE_INFORMATION, but the > server may also update the file's AllocationSize. If the client keeps > the old cached i_blocks value after fallocate, the swapfile hole check > can still see: > > i_blocks * 512 < i_size > > and reject the file as sparse. > > This shows up in xfstests generic/496 as: > > generic/496 [not run] fallocated swap not supported here > > After a successful EOF-extending fallocate, query FILE_ALL_INFORMATION on > the open handle and update i_blocks from the returned AllocationSize. If > the query fails, leave the fallocate result unchanged and force a later > attribute revalidation by setting cifsi->time to zero. > > With this client-side refresh, and with a server that really allocates the > fallocated range, for example Samba configured with: > > [scratch_share] > strict allocate = yes > > generic/496 can pass the swapfile hole check. > > Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> > Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> > --- > fs/smb/client/smb2ops.c | 15 +++++++++++++++ > 1 file changed, 15 insertions(+) > > diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c > index d4875f9532b4..89230141b5dd 100644 > --- a/fs/smb/client/smb2ops.c > +++ b/fs/smb/client/smb2ops.c > @@ -3698,8 +3698,23 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, > rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, > cfile->fid.volatile_fid, cfile->pid, new_eof); > if (rc == 0) { > + struct smb2_file_all_info file_inf; > + u64 asize; > + int qrc; > + > 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; > } > -- > 2.43.0 > -- Thanks, Steve ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/3] smb/client: refresh allocation size after fallocate 2026-06-07 15:35 ` Steve French @ 2026-06-07 16:20 ` hehuiwen 2026-06-08 15:28 ` hehuiwen 1 sibling, 0 replies; 8+ messages in thread From: hehuiwen @ 2026-06-07 16:20 UTC (permalink / raw) To: Steve French Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, senozhatsky, dhowells, metze, chenxiaosong, linux-cifs Hi Steve, Thanks for testing. This may be related to the fallocate patch now refreshing i_blocks from the server-reported AllocationSize. It seems generic/568 may have passed before because the client-side i_blocks estimate was too optimistic, rather than because the fallocated range was really allocated. I need to verify this more carefully. I will look into it tomorrow. Thanks, Huiwen 在 2026/6/7 23:35, Steve French 写道: > This regresses xfstests generic/568. > > generic/568 1s ... - output mismatch (see > /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad) > --- tests/generic/568.out 2025-08-22 09:54:32.333796989 -0500 > +++ /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad > 2026-06-07 10:32:28.896135808 -0500 > @@ -1,4 +1,4 @@ > QA output created by 568 > wrote 2/2 bytes at offset block_size - 1 > XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) > -OK: File did not grow. > +ERROR: File grew from 512 B to 1536 B when writing to the fallocated range. > ... > (Run 'diff -u /home/smfrench/xfstests-dev/tests/generic/568.out > /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad' to > see the entire diff) > > On Fri, Jun 5, 2026 at 11:36 AM Huiwen He <huiwen.he@linux.dev> wrote: >> >> From: Huiwen He <hehuiwen@kylinos.cn> >> >> SMB3 fallocate extends EOF using FILE_END_OF_FILE_INFORMATION, but the >> server may also update the file's AllocationSize. If the client keeps >> the old cached i_blocks value after fallocate, the swapfile hole check >> can still see: >> >> i_blocks * 512 < i_size >> >> and reject the file as sparse. >> >> This shows up in xfstests generic/496 as: >> >> generic/496 [not run] fallocated swap not supported here >> >> After a successful EOF-extending fallocate, query FILE_ALL_INFORMATION on >> the open handle and update i_blocks from the returned AllocationSize. If >> the query fails, leave the fallocate result unchanged and force a later >> attribute revalidation by setting cifsi->time to zero. >> >> With this client-side refresh, and with a server that really allocates the >> fallocated range, for example Samba configured with: >> >> [scratch_share] >> strict allocate = yes >> >> generic/496 can pass the swapfile hole check. >> >> Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> >> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> >> --- >> fs/smb/client/smb2ops.c | 15 +++++++++++++++ >> 1 file changed, 15 insertions(+) >> >> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c >> index d4875f9532b4..89230141b5dd 100644 >> --- a/fs/smb/client/smb2ops.c >> +++ b/fs/smb/client/smb2ops.c >> @@ -3698,8 +3698,23 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, >> rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, >> cfile->fid.volatile_fid, cfile->pid, new_eof); >> if (rc == 0) { >> + struct smb2_file_all_info file_inf; >> + u64 asize; >> + int qrc; >> + >> 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; >> } >> -- >> 2.43.0 >> > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2 3/3] smb/client: refresh allocation size after fallocate 2026-06-07 15:35 ` Steve French 2026-06-07 16:20 ` hehuiwen @ 2026-06-08 15:28 ` hehuiwen 1 sibling, 0 replies; 8+ messages in thread From: hehuiwen @ 2026-06-08 15:28 UTC (permalink / raw) To: Steve French Cc: linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm, senozhatsky, dhowells, metze, chenxiaosong, linux-cifs Hi Steve: Thanks for your feedback. I've been tied up with other tasks today and haven't had a chance to dig into the details yet. My current understanding is that these failures(213/485/568/701) are mostly around CIFS fallocate semantics. My swap series changes CIFS i_blocks accounting so that we no longer treat EOF extension as real allocation. That makes the swapfile hole check more accurate, but it can also expose existing fallocate behavior where the server reports success without actually allocating the requested range. generic/568 expects a later write into a fallocated range not to increase st_blocks, and generic/701 also depends on fallocate/truncate reporting allocation correctly. I also checked generic/213. It passes in my setup. With Samba "strict allocate = yes", the server attempts to reserve space during fallocate, so an allocation larger than the available space fails with ENOSPC, which is what the test expects. For generic/568, I did a temporary local experiment where CIFS requests an AllocationSize update after EOF-extending fallocate and then refreshes the cached allocation size. With that, generic/568 can pass locally. So I need more time to understand and verify how CIFS should preserve the semantics of all fallocate modes before sending an updated patch. Thanks, Huiwen 在 2026/6/7 23:35, Steve French 写道: > This regresses xfstests generic/568. > > generic/568 1s ... - output mismatch (see > /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad) > --- tests/generic/568.out 2025-08-22 09:54:32.333796989 -0500 > +++ /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad > 2026-06-07 10:32:28.896135808 -0500 > @@ -1,4 +1,4 @@ > QA output created by 568 > wrote 2/2 bytes at offset block_size - 1 > XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) > -OK: File did not grow. > +ERROR: File grew from 512 B to 1536 B when writing to the fallocated range. > ... > (Run 'diff -u /home/smfrench/xfstests-dev/tests/generic/568.out > /home/smfrench/xfstests-dev/results//sambamfs/generic/568.out.bad' to > see the entire diff) > > On Fri, Jun 5, 2026 at 11:36 AM Huiwen He <huiwen.he@linux.dev> wrote: >> >> From: Huiwen He <hehuiwen@kylinos.cn> >> >> SMB3 fallocate extends EOF using FILE_END_OF_FILE_INFORMATION, but the >> server may also update the file's AllocationSize. If the client keeps >> the old cached i_blocks value after fallocate, the swapfile hole check >> can still see: >> >> i_blocks * 512 < i_size >> >> and reject the file as sparse. >> >> This shows up in xfstests generic/496 as: >> >> generic/496 [not run] fallocated swap not supported here >> >> After a successful EOF-extending fallocate, query FILE_ALL_INFORMATION on >> the open handle and update i_blocks from the returned AllocationSize. If >> the query fails, leave the fallocate result unchanged and force a later >> attribute revalidation by setting cifsi->time to zero. >> >> With this client-side refresh, and with a server that really allocates the >> fallocated range, for example Samba configured with: >> >> [scratch_share] >> strict allocate = yes >> >> generic/496 can pass the swapfile hole check. >> >> Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> >> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> >> --- >> fs/smb/client/smb2ops.c | 15 +++++++++++++++ >> 1 file changed, 15 insertions(+) >> >> diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c >> index d4875f9532b4..89230141b5dd 100644 >> --- a/fs/smb/client/smb2ops.c >> +++ b/fs/smb/client/smb2ops.c >> @@ -3698,8 +3698,23 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon, >> rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, >> cfile->fid.volatile_fid, cfile->pid, new_eof); >> if (rc == 0) { >> + struct smb2_file_all_info file_inf; >> + u64 asize; >> + int qrc; >> + >> 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; >> } >> -- >> 2.43.0 >> > > ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-08 15:28 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-05 16:35 [PATCH v2 0/3] smb: client: fix i_blocks accounting for swapfile xfstests Huiwen He 2026-06-05 16:35 ` [PATCH v2 1/3] smb/client: update i_blocks after contiguous writes Huiwen He 2026-06-05 16:35 ` [PATCH v2 2/3] smb/client: do not account EOF extension as allocation Huiwen He 2026-06-07 15:33 ` Steve French 2026-06-05 16:35 ` [PATCH v2 3/3] smb/client: refresh allocation size after fallocate Huiwen He 2026-06-07 15:35 ` Steve French 2026-06-07 16:20 ` hehuiwen 2026-06-08 15:28 ` hehuiwen
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.