Re: [PATCH v4 6/7] smb/client: emulate small mode 0 fallocate ranges at or past EOF
Steve French <[email protected]>
| Newsgroups | org.kernel.vger.linux-cifs |
|---|---|
| Message-ID | <CAH2r5msQgF_ktpuAbebZzhd+=u7yu9GO1+9ePpRmwB4cLO6FTQ@mail.gmail.com> |
Running to Samba I have always been getting a failure with generic/213
(with or without this patch). Any ideas?
generic/213 - output mismatch (see
/home/smfrench/xfstests-dev/results//sambamfs/generic/213.out.bad)
--- tests/generic/213.out 2026-06-16 19:54:18.366737595 -0500
+++ /home/smfrench/xfstests-dev/results//sambamfs/generic/213.out.bad
2026-06-27 12:37:15.973294906 -0500
@@ -1,4 +1,3 @@
QA output created by 213
We should get: fallocate: No space left on device
Strangely, xfs_io sometimes says "Success" when something went wrong, FYI
-fallocate: No space left on device
On Fri, Jun 26, 2026 at 8:48 AM Huiwen He <[email protected]> wrote:
>
> From: Huiwen He <[email protected]>
>
> The xfstest generic/213 has a mode 0 fallocate case like:
>
> falloc 0 1G
> falloc 2G 1M
> truncate 3G
>
> The expected layout is:
>
> allocated [0, 1G)
> hole [1G, 2G)
> allocated [2G, 2G + 1M)
>
> Current CIFS can pass this test while allocating the intervening hole.
> Before this change, the server-side layout was effectively:
>
> allocated [0, 1G)
> allocated [1G, 2G + 1M)
>
> so [1G, 2G) was allocated as part of the second extent.
>
> Emulate small mode 0 fallocate ranges that start at or past EOF by writing
> zeroes only to the requested range. For off > old_eof, this preserves the
> hole between old_eof and off. Keep this emulation limited to 1 MiB so the
> client-side I/O cost remains bounded; larger unsupported ranges remain
> rejected.
>
> After this change, the same pattern gives the intended layout:
>
> allocated [0, 1G)
> hole [1G, 2G)
> allocated [2G, 2G + 1M)
>
> This changes generic/213 from a false pass into a range-correct pass.
>
> Signed-off-by: Huiwen He <[email protected]>
> Reviewed-by: ChenXiaoSong <[email protected]>
> ---
> 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 311709d73798..02ecba985daa 100644
> --- a/fs/smb/client/smb2ops.c
> +++ b/fs/smb/client/smb2ops.c
> @@ -3693,18 +3693,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);
> @@ -3714,11 +3718,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) {
> rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
> if (rc)
> --
> 2.43.0
>
--
Thanks,
Steve