From: "Darrick J. Wong" <djwong@kernel.org>
To: daejun7.park@samsung.com
Cc: Chuck Lever <cel@kernel.org>, Jeff Layton <jlayton@kernel.org>,
NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
Christoph Hellwig <hch@lst.de>,
Sergey Bashirov <sergeybashirov@gmail.com>,
Carlos Maiolino <cem@kernel.org>,
Amir Goldstein <amir73il@gmail.com>,
linux-nfs@vger.kernel.org, linux-xfs@vger.kernel.org,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] nfsd: do not return overlapping extents in a block layout
Date: Wed, 7 Oct 2026 09:51:01 -0700 [thread overview]
Message-ID: <20261007165101.GD2705364@frogsfrogsfrogs> (raw)
In-Reply-To: <20261007-nfsd-block-trim-v1-1-53370f97047a@samsung.com>
On Wed, Oct 07, 2026 at 11:03:01AM +0900, Daejun Park via B4 Relay wrote:
> From: Daejun Park <daejun7.park@samsung.com>
>
> Since commit cc6c40e09d7b ("NFSD/blocklayout: Support multiple extents
> per LAYOUTGET"), nfsd4_block_proc_layoutget() calls ->map_blocks once
> per extent of a LAYOUTGET, each time for the range left after the
> previous extent. nfsd4_block_map_extent() takes a mapping that starts at
> the offset asked for or below it, but only the first extent may start
> below it. A later extent that starts below its offset overlaps the
> extents before it, which RFC 5663 section 2.3.1 does not allow. The
> Linux client rejects such a layout (verify_extent() returns -EIO), and
> the I/O that needed it fails. The block and SCSI layouts share this
> code.
>
> XFS, the only ->map_blocks implementation, used to map the whole extent
> that contains the offset. Together with one call per extent, that gave
> overlapping layouts in v6.19 and v7.0: allocating the blocks for one
> extent of a write layout could merge the extent that the next call
> starts in with the unwritten extents before it. Since commit
> 36ca6f11424a ("xfs: fix overlapping extents returned for pNFS
> LAYOUTGET"), XFS does not map below the offset, so this patch changes
> nothing with current XFS. It only stops nfsd from relying on that: trim
> each extent after the first so that it starts at the offset asked for,
> and move its volume offset by the same amount unless it is a NONE_DATA
> extent, which has none.
>
> nfsd leaves the end of a mapping alone, as only the filesystem knows
> where it should end. It does need a mapping that contains the offset
> asked for, which RFC 5663 section 2.3.1 also requires of the first
> extent. A mapping that does not would leave a gap in the layout or make
> the length computed from it wrap around, so warn and return
> NFS4ERR_LAYOUTUNAVAILABLE; the client then does the I/O through the
> metadata server. iomap_iter_done() has the same check, as a
> WARN_ON_ONCE(), for ->iomap_begin(), and nfsd4_block_map_extent()
> already warns and fails this way for a mapping of an unexpected type.
> Document in exportfs_block.h that the mapping must contain the offset.
>
> On a test kernel whose xfs_fs_map_blocks() maps with XFS_BMAPI_ENTIRE
> and does not trim, as XFS did before that commit, a write layout over a
> hole between two unwritten blocks comes back as 0+4096, 4096+4096 and
> 0+12288, and the pynfs test BLOCK5 fails in five runs out of five.
> fstests generic/075, 091 and 263 over the block layout fail with an EIO
> or a zero-length O_DIRECT write, and bl_alloc_lseg() on the client
> returns -EIO three times. With this patch on top, the third extent is
> 8192+4096, BLOCK5 passes in five runs out of five, generic/091 and 263
> pass, generic/075 fails with the fsx "Size error" that it also fails
> with on nfsd-testing, and bl_alloc_lseg() returns no error.
>
> Suggested-by: Darrick J. Wong <djwong@kernel.org>
> Link: https://lore.kernel.org/r/20261006051324.GV2705364@frogsfrogsfrogs
> Link: https://lore.kernel.org/r/20261006152333.GO1615495@frogsfrogsfrogs
> Signed-off-by: Daejun Park <daejun7.park@samsung.com>
> ---
> Darrick asked whether nfsd should trim the lower end of a mapping that
> starts below the offset asked for, or warn and fail. This patch trims
> such a mapping for every extent but the first, and warns only when a
> mapping does not contain the offset at all. The XFS patch his question
> was about also trims, in xfs_fs_map_blocks(). The two patches do not
> depend on each other:
> https://lore.kernel.org/r/20261006003425epcms2p586728e55ff5f9bbabc506b672fc4a421@epcms2p5
>
> No stable backport is needed, which is also why there is no Fixes: tag
> for cc6c40e09d7b. Overlapping extents need both the per-extent calls of
> cc6c40e09d7b (v6.19) and XFS_BMAPI_ENTIRE in xfs_fs_map_blocks(), which
> 36ca6f11424a removed in v7.1. Only 6.19.y and 7.0.y have both, and
> neither is maintained any more; 6.18.y and older call ->map_blocks once
> per LAYOUTGET.
>
> Tested on nfsd-testing 56589cdb5881 with three QEMU VMs (an NVMe/TCP
> target, the server with nfsd and XFS, and a client), over the block
> layout only. With only this patch, nfsd-testing gives the same BLOCK5
> and fstests results as without it. With KASAN and lockdep, the test
> kernel with this patch on top gives the same BLOCK5 and fstests results
> as above, with no warning. Trimming was seen only for the INVALID_DATA
> extents of BLOCK5; nothing counted trims during the fstests runs. The
> pynfs test BLOCK5 is at
> https://lore.kernel.org/r/20261006002622epcms2p38e492aef17fdf79e48b05c9aada2918d@epcms2p3
> ---
> fs/nfsd/blocklayout.c | 28 ++++++++++++++++++++++++++--
> include/linux/exportfs_block.h | 2 ++
> 2 files changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> index df02cf746..1aabae003 100644
> --- a/fs/nfsd/blocklayout.c
> +++ b/fs/nfsd/blocklayout.c
> @@ -20,8 +20,8 @@
>
>
> /*
> - * Get an extent from the file system that starts at offset or below
> - * and may be shorter than the requested length.
> + * Get an extent from the file system that contains offset. It may start
> + * below offset and may be shorter than the requested length.
Nitpicking here, but the extent could extend beyond than the requested
@offset/@length range too, right? Shouldn't the comment say that, since
the header comment allows for both cases, right?
With that fixed,
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> */
> static __be32
> nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
> @@ -41,6 +41,13 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
> return nfserrno(error);
> }
>
> + if (WARN_ONCE(iomap.offset > offset ||
> + offset - iomap.offset >= iomap.length,
> + "pnfsd: %s ino %llu: filesystem returned extent %lld+%llu for offset %llu\n",
> + sb->s_id, inode->i_ino, iomap.offset, iomap.length,
> + offset))
> + return nfserr_layoutunavailable;
> +
> switch (iomap.type) {
> case IOMAP_MAPPED:
> if (iomode == IOMODE_READ)
> @@ -147,6 +154,23 @@ nfsd4_block_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode,
> if (nfserr != nfs_ok)
> goto out_error;
>
> + /*
> + * Each extent after the first was mapped for the range that
> + * starts where the previous extent ends, but the filesystem
> + * may return a mapping that starts below that point. Trim
> + * it, as RFC 5663 section 2.3.1 does not allow extents to
> + * overlap. nfsd4_block_map_extent() made sure the mapping
> + * contains offset. NONE_DATA extents have no volume offset.
> + */
> + if (i > 0 && bex->foff < offset) {
> + u64 skip = offset - bex->foff;
> +
> + bex->foff = offset;
> + bex->len -= skip;
> + if (bex->es != PNFS_BLOCK_NONE_DATA)
> + bex->soff += skip;
> + }
> +
> bex_length = bex->len - (offset - bex->foff);
> if (bex_length >= length) {
> bl->nr_extents = i + 1;
> diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
> index de519b7b5..21e61fc01 100644
> --- a/include/linux/exportfs_block.h
> +++ b/include/linux/exportfs_block.h
> @@ -44,6 +44,8 @@ struct exportfs_block_ops {
> /*
> * Map blocks for direct block access.
> * If @write is %true, also allocate the blocks for the range if needed.
> + * The mapping returned must contain @offset. It may start before
> + * @offset and may end before or after @offset + @len.
> */
> int (*map_blocks)(struct inode *inode, loff_t offset, u64 len,
> struct iomap *iomap, bool write,
>
> ---
> base-commit: 56589cdb58819ce54decedbbfddf231d94b5ce41
> change-id: 20261007-nfsd-block-trim-81a6a8b066c9
>
> Best regards,
> --
> Daejun Park <daejun7.park@samsung.com>
>
>
>
next prev parent reply other threads:[~2026-10-07 16:51 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-07 2:03 [PATCH] nfsd: do not return overlapping extents in a block layout Daejun Park via B4 Relay
2026-10-07 2:03 ` Daejun Park
2026-10-07 13:11 ` Christoph Hellwig
2026-10-08 1:43 ` Daejun Park
2026-10-07 14:58 ` Chuck Lever
2026-10-07 16:51 ` Darrick J. Wong [this message]
2026-10-08 1:47 ` Daejun Park
2026-10-08 14:14 ` (2) " Chuck Lever
2026-10-12 2:52 ` Daejun Park
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=20261007165101.GD2705364@frogsfrogsfrogs \
--to=djwong@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=amir73il@gmail.com \
--cc=cel@kernel.org \
--cc=cem@kernel.org \
--cc=daejun7.park@samsung.com \
--cc=hch@lst.de \
--cc=jlayton@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=linux-xfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=sergeybashirov@gmail.com \
--cc=tom@talpey.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.