mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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>
> 
> 
> 

  parent reply	other threads:[~2026-10-07 16:51 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-07  2:03 Daejun Park via B4 Relay
2026-10-07 13:11 ` Christoph Hellwig
2026-10-07 14:58 ` Chuck Lever
2026-10-07 16:51 ` Darrick J. Wong [this message]
     [not found] ` <CGME20261007131141epcas2p46e1bc1592a0789298bc9f7e13c7ad393@epcms2p8>
2026-10-08  1:43   ` Daejun Park
     [not found] ` <CGME20261007165105epcas2p264fc1fd07bab1fb877a6695062f48322@epcms2p7>
2026-10-08  1:47   ` Daejun Park
2026-10-08 14:14     ` (2) " Chuck Lever

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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®