public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
* cleanup block-style layouts exports
@ 2026-03-31 15:33 Christoph Hellwig
  2026-03-31 15:33 ` [PATCH 1/4] nfsd/blocklayout: always ignore loca_time_modify Christoph Hellwig
                   ` (4 more replies)
  0 siblings, 5 replies; 21+ messages in thread
From: Christoph Hellwig @ 2026-03-31 15:33 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

Hi all,

this series cleanups the exportfs support for block-style layouts that
provide direct block device access.  This is preparation for supporting
exportfs of more than a single device per file system.

Changes since the multi-device export series:
 - check for NULL bops in nfsd4_setup_layout_type
 - clearly document why we are ignoring loca_time_modify

Diffstat:
 MAINTAINERS                    |    2 
 fs/nfsd/blocklayout.c          |   37 +++++++----------
 fs/nfsd/export.c               |    3 -
 fs/nfsd/nfs4layouts.c          |   29 +++----------
 fs/xfs/xfs_export.c            |    4 -
 fs/xfs/xfs_pnfs.c              |   44 ++++++++++++++------
 fs/xfs/xfs_pnfs.h              |   11 ++---
 include/linux/exportfs.h       |   25 +++--------
 include/linux/exportfs_block.h |   88 +++++++++++++++++++++++++++++++++++++++++
 9 files changed, 162 insertions(+), 81 deletions(-)

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

* [PATCH 1/4] nfsd/blocklayout: always ignore loca_time_modify
  2026-03-31 15:33 cleanup block-style layouts exports Christoph Hellwig
@ 2026-03-31 15:33 ` Christoph Hellwig
  2026-03-31 17:09   ` Jeff Layton
  2026-03-31 15:33 ` [PATCH 2/4] exportfs: split out the ops for layout-based block device access Christoph Hellwig
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 21+ messages in thread
From: Christoph Hellwig @ 2026-03-31 15:33 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

RFC 8881 Section 18.42 makes it clear that the client provided timestamp
is a "may" condition, and clients that want to force a specific timestamp
should send a separate SETATTR in the compound.

Since commit b82f92d5dd1a ("fs: have setattr_copy handle multigrain
timestamps appropriately") the ia_mtime value is ignored by file
systems using multi-grain timestamps like XFS, which is the only
file system supporting blocklayout exports right now, so make that
explicit in NFSD as well.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/nfsd/blocklayout.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index a7cfba29990e..5ee5735b39bb 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -179,15 +179,20 @@ static __be32
 nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
 		struct iomap *iomaps, int nr_iomaps)
 {
-	struct timespec64 mtime = inode_get_mtime(inode);
 	struct iattr iattr = { .ia_valid = 0 };
 	int error;
 
-	if (lcp->lc_mtime.tv_nsec == UTIME_NOW ||
-	    timespec64_compare(&lcp->lc_mtime, &mtime) < 0)
-		lcp->lc_mtime = current_time(inode);
+	/*
+	 * This ignores the client provided mtime in loca_time_modify, as a
+	 * fully client specified mtime doesn't really fit into the Linux
+	 * multi-grain timestamp architecture.
+	 *
+	 * RFC 8881 Section 18.42 makes it clear that the client provided
+	 * timestamp is a "may" condition, and clients that want to force a
+	 * specific timestamp should send a separate SETATTR in the compound.
+	 */
 	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
-	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
+	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = current_time(inode);
 
 	if (lcp->lc_size_chg) {
 		iattr.ia_valid |= ATTR_SIZE;
-- 
2.47.3


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

* [PATCH 2/4] exportfs: split out the ops for layout-based block device access
  2026-03-31 15:33 cleanup block-style layouts exports Christoph Hellwig
  2026-03-31 15:33 ` [PATCH 1/4] nfsd/blocklayout: always ignore loca_time_modify Christoph Hellwig
@ 2026-03-31 15:33 ` Christoph Hellwig
  2026-03-31 17:24   ` Jeff Layton
  2026-03-31 18:07   ` Chuck Lever
  2026-03-31 15:33 ` [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks Christoph Hellwig
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 21+ messages in thread
From: Christoph Hellwig @ 2026-03-31 15:33 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

The support to grant layouts for direct block device access works
at a very different layer than the rest of exports.  Split the methods
for it into a separate struct, and move that into a separate header
to better split things out.  The pointer to the new operation vector
is kept in export_operations to avoid bloating the super_block.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 MAINTAINERS                    |  2 +-
 fs/nfsd/blocklayout.c          | 14 ++++++------
 fs/nfsd/nfs4layouts.c          |  9 ++++----
 fs/xfs/xfs_export.c            |  4 +---
 fs/xfs/xfs_pnfs.c              | 12 ++++++++---
 fs/xfs/xfs_pnfs.h              | 11 +++++-----
 include/linux/exportfs.h       | 25 +++++++---------------
 include/linux/exportfs_block.h | 39 ++++++++++++++++++++++++++++++++++
 8 files changed, 74 insertions(+), 42 deletions(-)
 create mode 100644 include/linux/exportfs_block.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c3fe46d7c4bc..b531b87a007e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9856,7 +9856,7 @@ S:	Supported
 F:	Documentation/filesystems/nfs/exporting.rst
 F:	fs/exportfs/
 F:	fs/fhandle.c
-F:	include/linux/exportfs.h
+F:	include/linux/exportfs*.h
 
 FILESYSTEMS [IDMAPPED MOUNTS]
 M:	Christian Brauner <brauner@kernel.org>
diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index 5ee5735b39bb..3cc3b47361e2 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -2,7 +2,7 @@
 /*
  * Copyright (c) 2014-2016 Christoph Hellwig.
  */
-#include <linux/exportfs.h>
+#include <linux/exportfs_block.h>
 #include <linux/iomap.h>
 #include <linux/slab.h>
 #include <linux/pr.h>
@@ -32,8 +32,8 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
 	u32 device_generation = 0;
 	int error;
 
-	error = sb->s_export_op->map_blocks(inode, offset, length, &iomap,
-			iomode != IOMODE_READ, &device_generation);
+	error = sb->s_export_op->block_ops->map_blocks(inode, offset, length,
+			&iomap, iomode != IOMODE_READ, &device_generation);
 	if (error) {
 		if (error == -ENXIO)
 			return nfserr_layoutunavailable;
@@ -199,8 +199,8 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
 		iattr.ia_size = lcp->lc_newsize;
 	}
 
-	error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,
-			nr_iomaps, &iattr);
+	error = inode->i_sb->s_export_op->block_ops->commit_blocks(inode,
+			iomaps, nr_iomaps, &iattr);
 	kfree(iomaps);
 	return nfserrno(error);
 }
@@ -223,8 +223,8 @@ nfsd4_block_get_device_info_simple(struct super_block *sb,
 
 	b->type = PNFS_BLOCK_VOLUME_SIMPLE;
 	b->simple.sig_len = PNFS_BLOCK_UUID_LEN;
-	return sb->s_export_op->get_uuid(sb, b->simple.sig, &b->simple.sig_len,
-			&b->simple.offset);
+	return sb->s_export_op->block_ops->get_uuid(sb, b->simple.sig,
+			&b->simple.sig_len, &b->simple.offset);
 }
 
 static __be32
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index ad7af8cfcf1f..c53eb67969eb 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -3,6 +3,7 @@
  * Copyright (c) 2014 Christoph Hellwig.
  */
 #include <linux/blkdev.h>
+#include <linux/exportfs_block.h>
 #include <linux/kmod.h>
 #include <linux/file.h>
 #include <linux/jhash.h>
@@ -127,6 +128,7 @@ void nfsd4_setup_layout_type(struct svc_export *exp)
 {
 #if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
 	struct super_block *sb = exp->ex_path.mnt->mnt_sb;
+	struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
 #endif
 
 	if (!(exp->ex_flags & NFSEXP_PNFS))
@@ -136,14 +138,11 @@ void nfsd4_setup_layout_type(struct svc_export *exp)
 	exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
 #endif
 #ifdef CONFIG_NFSD_BLOCKLAYOUT
-	if (sb->s_export_op->get_uuid &&
-	    sb->s_export_op->map_blocks &&
-	    sb->s_export_op->commit_blocks)
+	if (bops && bops->get_uuid && bops->map_blocks && bops->commit_blocks)
 		exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
 #endif
 #ifdef CONFIG_NFSD_SCSILAYOUT
-	if (sb->s_export_op->map_blocks &&
-	    sb->s_export_op->commit_blocks &&
+	if (bops && bops->map_blocks && bops->commit_blocks &&
 	    sb->s_bdev &&
 	    sb->s_bdev->bd_disk->fops->pr_ops &&
 	    sb->s_bdev->bd_disk->fops->get_unique_id)
diff --git a/fs/xfs/xfs_export.c b/fs/xfs/xfs_export.c
index e3e3c3c89840..9b2ad3786b19 100644
--- a/fs/xfs/xfs_export.c
+++ b/fs/xfs/xfs_export.c
@@ -244,8 +244,6 @@ const struct export_operations xfs_export_operations = {
 	.get_parent		= xfs_fs_get_parent,
 	.commit_metadata	= xfs_fs_nfs_commit_metadata,
 #ifdef CONFIG_EXPORTFS_BLOCK_OPS
-	.get_uuid		= xfs_fs_get_uuid,
-	.map_blocks		= xfs_fs_map_blocks,
-	.commit_blocks		= xfs_fs_commit_blocks,
+	.block_ops		= &xfs_export_block_ops,
 #endif
 };
diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index 221e55887a2a..a52978f6fb76 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -49,7 +49,7 @@ xfs_break_leased_layouts(
  * Get a unique ID including its location so that the client can identify
  * the exported device.
  */
-int
+static int
 xfs_fs_get_uuid(
 	struct super_block	*sb,
 	u8			*buf,
@@ -104,7 +104,7 @@ xfs_fs_map_update_inode(
 /*
  * Get a layout for the pNFS client.
  */
-int
+static int
 xfs_fs_map_blocks(
 	struct inode		*inode,
 	loff_t			offset,
@@ -252,7 +252,7 @@ xfs_pnfs_validate_isize(
  * to manually flush the cache here similar to what the fsync code path does
  * for datasyncs on files that have no dirty metadata.
  */
-int
+static int
 xfs_fs_commit_blocks(
 	struct inode		*inode,
 	struct iomap		*maps,
@@ -332,3 +332,9 @@ xfs_fs_commit_blocks(
 	xfs_iunlock(ip, XFS_IOLOCK_EXCL);
 	return error;
 }
+
+struct exportfs_block_ops xfs_export_block_ops = {
+	.get_uuid		= xfs_fs_get_uuid,
+	.map_blocks		= xfs_fs_map_blocks,
+	.commit_blocks		= xfs_fs_commit_blocks,
+};
diff --git a/fs/xfs/xfs_pnfs.h b/fs/xfs/xfs_pnfs.h
index 940c6c2ad88c..9c289e98f2a6 100644
--- a/fs/xfs/xfs_pnfs.h
+++ b/fs/xfs/xfs_pnfs.h
@@ -2,13 +2,9 @@
 #ifndef _XFS_PNFS_H
 #define _XFS_PNFS_H 1
 
-#ifdef CONFIG_EXPORTFS_BLOCK_OPS
-int xfs_fs_get_uuid(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
-int xfs_fs_map_blocks(struct inode *inode, loff_t offset, u64 length,
-		struct iomap *iomap, bool write, u32 *device_generation);
-int xfs_fs_commit_blocks(struct inode *inode, struct iomap *maps, int nr_maps,
-		struct iattr *iattr);
+#include <linux/exportfs_block.h>
 
+#ifdef CONFIG_EXPORTFS_BLOCK_OPS
 int xfs_break_leased_layouts(struct inode *inode, uint *iolock,
 		bool *did_unlock);
 #else
@@ -18,4 +14,7 @@ xfs_break_leased_layouts(struct inode *inode, uint *iolock, bool *did_unlock)
 	return 0;
 }
 #endif /* CONFIG_EXPORTFS_BLOCK_OPS */
+
+extern struct exportfs_block_ops xfs_export_block_ops;
+
 #endif /* _XFS_PNFS_H */
diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
index 8bcdba28b406..f07ce833fba3 100644
--- a/include/linux/exportfs.h
+++ b/include/linux/exportfs.h
@@ -6,9 +6,8 @@
 #include <linux/path.h>
 
 struct dentry;
-struct iattr;
+struct exportfs_block_ops;
 struct inode;
-struct iomap;
 struct super_block;
 struct vfsmount;
 
@@ -260,19 +259,13 @@ struct handle_to_path_ctx {
  * @commit_metadata:
  *    @commit_metadata should commit metadata changes to stable storage.
  *
- * @get_uuid:
- *    Get a filesystem unique signature exposed to clients.
- *
- * @map_blocks:
- *    Map and, if necessary, allocate blocks for a layout.
- *
- * @commit_blocks:
- *    Commit blocks in a layout once the client is done with them.
- *
  * @flags:
  *    Allows the filesystem to communicate to nfsd that it may want to do things
  *    differently when dealing with it.
  *
+ * @block_ops:
+ *    Operations for layout grants to block on the underlying device.
+ *
  * Locking rules:
  *    get_parent is called with child->d_inode->i_rwsem down
  *    get_name is not (which is possibly inconsistent)
@@ -290,12 +283,6 @@ struct export_operations {
 	struct dentry * (*get_parent)(struct dentry *child);
 	int (*commit_metadata)(struct inode *inode);
 
-	int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
-	int (*map_blocks)(struct inode *inode, loff_t offset,
-			  u64 len, struct iomap *iomap,
-			  bool write, u32 *device_generation);
-	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
-			     int nr_iomaps, struct iattr *iattr);
 	int (*permission)(struct handle_to_path_ctx *ctx, unsigned int oflags);
 	struct file * (*open)(const struct path *path, unsigned int oflags);
 #define	EXPORT_OP_NOWCC			(0x1) /* don't collect v3 wcc data */
@@ -308,6 +295,10 @@ struct export_operations {
 #define EXPORT_OP_FLUSH_ON_CLOSE	(0x20) /* fs flushes file data on close */
 #define EXPORT_OP_NOLOCKS		(0x40) /* no file locking support */
 	unsigned long	flags;
+
+#ifdef CONFIG_EXPORTFS_BLOCK_OPS
+	struct exportfs_block_ops *block_ops;
+#endif
 };
 
 /**
diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
new file mode 100644
index 000000000000..1f52fea8e4dc
--- /dev/null
+++ b/include/linux/exportfs_block.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2014-2026 Christoph Hellwig.
+ *
+ * Support for exportfs-based layout grants for direct block device access.
+ */
+#ifndef LINUX_EXPORTFS_BLOCK_H
+#define LINUX_EXPORTFS_BLOCK_H 1
+
+#include <linux/types.h>
+
+struct iattr;
+struct inode;
+struct iomap;
+struct super_block;
+
+struct exportfs_block_ops {
+	/*
+	 * Get the in-band device unique signature exposed to clients.
+	 */
+	int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
+
+	/*
+	 * Map blocks for direct block access.
+	 * If @write is %true, also allocate the blocks for the range if needed.
+	 */
+	int (*map_blocks)(struct inode *inode, loff_t offset, u64 len,
+			struct iomap *iomap, bool write,
+			u32 *device_generation);
+
+	/*
+	 * Commit blocks previously handed out by ->map_blocks and written to by
+	 * the client.
+	 */
+	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
+			int nr_iomaps, struct iattr *iattr);
+};
+
+#endif /* LINUX_EXPORTFS_BLOCK_H */
-- 
2.47.3


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

* [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks
  2026-03-31 15:33 cleanup block-style layouts exports Christoph Hellwig
  2026-03-31 15:33 ` [PATCH 1/4] nfsd/blocklayout: always ignore loca_time_modify Christoph Hellwig
  2026-03-31 15:33 ` [PATCH 2/4] exportfs: split out the ops for layout-based block device access Christoph Hellwig
@ 2026-03-31 15:33 ` Christoph Hellwig
  2026-03-31 17:26   ` Jeff Layton
  2026-03-31 15:33 ` [PATCH 4/4] exportfs,nfsd: rework checking for layout-based block device access support Christoph Hellwig
  2026-03-31 17:33 ` cleanup block-style layouts exports Chuck Lever
  4 siblings, 1 reply; 21+ messages in thread
From: Christoph Hellwig @ 2026-03-31 15:33 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

The only thing ->commit_blocks really needs is the new size, with a magic
-1 placeholder 0 for "do not change the size" because it only ever
extends the size.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/nfsd/blocklayout.c          | 12 ++----------
 fs/xfs/xfs_pnfs.c              | 19 ++++++++++---------
 include/linux/exportfs_block.h |  3 +--
 3 files changed, 13 insertions(+), 21 deletions(-)

diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index 3cc3b47361e2..23c0e4d0ff34 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -179,7 +179,6 @@ static __be32
 nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
 		struct iomap *iomaps, int nr_iomaps)
 {
-	struct iattr iattr = { .ia_valid = 0 };
 	int error;
 
 	/*
@@ -191,16 +190,9 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
 	 * timestamp is a "may" condition, and clients that want to force a
 	 * specific timestamp should send a separate SETATTR in the compound.
 	 */
-	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
-	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = current_time(inode);
-
-	if (lcp->lc_size_chg) {
-		iattr.ia_valid |= ATTR_SIZE;
-		iattr.ia_size = lcp->lc_newsize;
-	}
-
 	error = inode->i_sb->s_export_op->block_ops->commit_blocks(inode,
-			iomaps, nr_iomaps, &iattr);
+			iomaps, nr_iomaps,
+			lcp->lc_size_chg ? lcp->lc_newsize : 0);
 	kfree(iomaps);
 	return nfserrno(error);
 }
diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index a52978f6fb76..fee782a3edbe 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -257,23 +257,22 @@ xfs_fs_commit_blocks(
 	struct inode		*inode,
 	struct iomap		*maps,
 	int			nr_maps,
-	struct iattr		*iattr)
+	loff_t			new_size)
 {
 	struct xfs_inode	*ip = XFS_I(inode);
 	struct xfs_mount	*mp = ip->i_mount;
 	struct xfs_trans	*tp;
+	struct timespec64	now;
 	bool			update_isize = false;
 	int			error, i;
 	loff_t			size;
 
-	ASSERT(iattr->ia_valid & (ATTR_ATIME|ATTR_CTIME|ATTR_MTIME));
-
 	xfs_ilock(ip, XFS_IOLOCK_EXCL);
 
 	size = i_size_read(inode);
-	if ((iattr->ia_valid & ATTR_SIZE) && iattr->ia_size > size) {
+	if (new_size > size) {
 		update_isize = true;
-		size = iattr->ia_size;
+		size = new_size;
 	}
 
 	for (i = 0; i < nr_maps; i++) {
@@ -318,11 +317,13 @@ xfs_fs_commit_blocks(
 	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
 	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
-	ASSERT(!(iattr->ia_valid & (ATTR_UID | ATTR_GID)));
-	setattr_copy(&nop_mnt_idmap, inode, iattr);
+	now = inode_set_ctime_current(inode);
+	inode_set_atime_to_ts(inode, now);
+	inode_set_mtime_to_ts(inode, now);
+
 	if (update_isize) {
-		i_size_write(inode, iattr->ia_size);
-		ip->i_disk_size = iattr->ia_size;
+		i_size_write(inode, new_size);
+		ip->i_disk_size = new_size;
 	}
 
 	xfs_trans_set_sync(tp);
diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
index 1f52fea8e4dc..d1dec4689b14 100644
--- a/include/linux/exportfs_block.h
+++ b/include/linux/exportfs_block.h
@@ -9,7 +9,6 @@
 
 #include <linux/types.h>
 
-struct iattr;
 struct inode;
 struct iomap;
 struct super_block;
@@ -33,7 +32,7 @@ struct exportfs_block_ops {
 	 * the client.
 	 */
 	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
-			int nr_iomaps, struct iattr *iattr);
+			int nr_iomaps, loff_t new_size);
 };
 
 #endif /* LINUX_EXPORTFS_BLOCK_H */
-- 
2.47.3


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

* [PATCH 4/4] exportfs,nfsd: rework checking for layout-based block device access support
  2026-03-31 15:33 cleanup block-style layouts exports Christoph Hellwig
                   ` (2 preceding siblings ...)
  2026-03-31 15:33 ` [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks Christoph Hellwig
@ 2026-03-31 15:33 ` Christoph Hellwig
  2026-03-31 17:52   ` Jeff Layton
  2026-03-31 18:00   ` Chuck Lever
  2026-03-31 17:33 ` cleanup block-style layouts exports Chuck Lever
  4 siblings, 2 replies; 21+ messages in thread
From: Christoph Hellwig @ 2026-03-31 15:33 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

Currently NFSD hard codes checking support for block-style layouts.
Lift the checks into a file system-helper and provide a exportfs-level
helper to implement the typical checks.

This prepares for supporting block layout export of multiple devices
per file system.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/nfsd/export.c               |  3 +-
 fs/nfsd/nfs4layouts.c          | 26 +++++------------
 fs/xfs/xfs_pnfs.c              | 13 +++++++++
 include/linux/exportfs_block.h | 52 +++++++++++++++++++++++++++++++++-
 4 files changed, 73 insertions(+), 21 deletions(-)

diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 8e8a76a44ff0..e20298f9212f 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -735,7 +735,8 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
 			goto out4;
 		err = 0;
 
-		nfsd4_setup_layout_type(&exp);
+		if (exp.ex_flags & NFSEXP_PNFS)
+			nfsd4_setup_layout_type(&exp);
 	}
 
 	expp = svc_export_lookup(&exp);
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index c53eb67969eb..7b849b637b5e 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -2,7 +2,6 @@
 /*
  * Copyright (c) 2014 Christoph Hellwig.
  */
-#include <linux/blkdev.h>
 #include <linux/exportfs_block.h>
 #include <linux/kmod.h>
 #include <linux/file.h>
@@ -126,28 +125,17 @@ nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
 
 void nfsd4_setup_layout_type(struct svc_export *exp)
 {
-#if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
 	struct super_block *sb = exp->ex_path.mnt->mnt_sb;
-	struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
-#endif
-
-	if (!(exp->ex_flags & NFSEXP_PNFS))
-		return;
+	expfs_block_layouts_t block_supported = exporfs_layouts_supported(sb);
 
-#ifdef CONFIG_NFSD_FLEXFILELAYOUT
-	exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
-#endif
-#ifdef CONFIG_NFSD_BLOCKLAYOUT
-	if (bops && bops->get_uuid && bops->map_blocks && bops->commit_blocks)
+	if (IS_ENABLED(CONFIG_NFSD_FLEXFILELAYOUT))
+		exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
+	if (IS_ENABLED(CONFIG_NFSD_BLOCKLAYOUT) &&
+	    (block_supported & EXPFS_BLOCK_IN_BAND_ID))
 		exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
-#endif
-#ifdef CONFIG_NFSD_SCSILAYOUT
-	if (bops && bops->map_blocks && bops->commit_blocks &&
-	    sb->s_bdev &&
-	    sb->s_bdev->bd_disk->fops->pr_ops &&
-	    sb->s_bdev->bd_disk->fops->get_unique_id)
+	if (IS_ENABLED(CONFIG_NFSD_SCSILAYOUT) &&
+	    (block_supported & EXPFS_BLOCK_OUT_OF_BAND_ID))
 		exp->ex_layout_types |= 1 << LAYOUT_SCSI;
-#endif
 }
 
 void nfsd4_close_layout(struct nfs4_layout_stateid *ls)
diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index fee782a3edbe..acefa0b99f53 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -13,6 +13,7 @@
 #include "xfs_bmap.h"
 #include "xfs_iomap.h"
 #include "xfs_pnfs.h"
+#include <linux/exportfs_block.h>
 
 /*
  * Ensure that we do not have any outstanding pNFS layouts that can be used by
@@ -45,6 +46,17 @@ xfs_break_leased_layouts(
 	return error;
 }
 
+static expfs_block_layouts_t
+xfs_fs_layouts_supported(
+	struct super_block	*sb)
+{
+	expfs_block_layouts_t	supported = EXPFS_BLOCK_IN_BAND_ID;
+
+	if (exportfs_bdev_supports_out_of_band_id(sb->s_bdev))
+		supported |= EXPFS_BLOCK_OUT_OF_BAND_ID;
+	return supported;
+}
+
 /*
  * Get a unique ID including its location so that the client can identify
  * the exported device.
@@ -335,6 +347,7 @@ xfs_fs_commit_blocks(
 }
 
 struct exportfs_block_ops xfs_export_block_ops = {
+	.layouts_supported	= xfs_fs_layouts_supported,
 	.get_uuid		= xfs_fs_get_uuid,
 	.map_blocks		= xfs_fs_map_blocks,
 	.commit_blocks		= xfs_fs_commit_blocks,
diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
index d1dec4689b14..8d5b0b0c5a82 100644
--- a/include/linux/exportfs_block.h
+++ b/include/linux/exportfs_block.h
@@ -7,13 +7,35 @@
 #ifndef LINUX_EXPORTFS_BLOCK_H
 #define LINUX_EXPORTFS_BLOCK_H 1
 
-#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/exportfs.h>
+#include <linux/fs.h>
 
 struct inode;
 struct iomap;
 struct super_block;
 
+/*
+ * There are the two types of block-style layout support:
+ *  - In-band implies a device identified by a unique cookie inside the actual
+ *    device address space checked by the ->get_uuid method as used by the pNFS
+ *    block layout.  This is a bit dangerous and deprecated.
+ *  - Out of band implies identification by out of band unique identifiers
+ *    specified by the storage protocol, which is much safer and used by the
+ *    pNFS SCSI/NVMe layouts.
+ */
+typedef unsigned int __bitwise expfs_block_layouts_t;
+#define EXPFS_BLOCK_FLAG(__bit) \
+	((__force expfs_block_layouts_t)(1u << __bit))
+#define EXPFS_BLOCK_IN_BAND_ID		EXPFS_BLOCK_FLAG(0)
+#define EXPFS_BLOCK_OUT_OF_BAND_ID	EXPFS_BLOCK_FLAG(1)
+
 struct exportfs_block_ops {
+	/*
+	 * Returns the EXPFS_BLOCK_* bitmap of supported layout types.
+	 */
+	expfs_block_layouts_t (*layouts_supported)(struct super_block *sb);
+
 	/*
 	 * Get the in-band device unique signature exposed to clients.
 	 */
@@ -35,4 +57,32 @@ struct exportfs_block_ops {
 			int nr_iomaps, loff_t new_size);
 };
 
+static inline bool
+exportfs_bdev_supports_out_of_band_id(struct block_device *bdev)
+{
+	return bdev->bd_disk->fops->pr_ops &&
+		bdev->bd_disk->fops->get_unique_id;
+}
+
+#ifdef CONFIG_EXPORTFS_BLOCK_OPS
+static inline expfs_block_layouts_t
+exporfs_layouts_supported(struct super_block *sb)
+{
+	struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
+
+	if (!bops ||
+	    !bops->layouts_supported ||
+	    WARN_ON_ONCE(!bops->map_blocks) ||
+	    WARN_ON_ONCE(!bops->commit_blocks))
+		return 0;
+	return bops->layouts_supported(sb);
+}
+#else
+static inline expfs_block_layouts_t
+exporfs_layouts_supported(struct super_block *sb)
+{
+	return 0;
+}
+#endif /* CONFIG_EXPORTFS_BLOCK_OPS */
+
 #endif /* LINUX_EXPORTFS_BLOCK_H */
-- 
2.47.3


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

* Re: [PATCH 1/4] nfsd/blocklayout: always ignore loca_time_modify
  2026-03-31 15:33 ` [PATCH 1/4] nfsd/blocklayout: always ignore loca_time_modify Christoph Hellwig
@ 2026-03-31 17:09   ` Jeff Layton
  2026-04-01 14:25     ` Christoph Hellwig
  0 siblings, 1 reply; 21+ messages in thread
From: Jeff Layton @ 2026-03-31 17:09 UTC (permalink / raw)
  To: Christoph Hellwig, Chuck Lever, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

On Tue, 2026-03-31 at 17:33 +0200, Christoph Hellwig wrote:
> RFC 8881 Section 18.42 makes it clear that the client provided timestamp
> is a "may" condition, and clients that want to force a specific timestamp
> should send a separate SETATTR in the compound.
> 
> Since commit b82f92d5dd1a ("fs: have setattr_copy handle multigrain
> timestamps appropriately") the ia_mtime value is ignored by file
> systems using multi-grain timestamps like XFS, which is the only
> file system supporting blocklayout exports right now, so make that
> explicit in NFSD as well.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/nfsd/blocklayout.c | 15 ++++++++++-----
>  1 file changed, 10 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> index a7cfba29990e..5ee5735b39bb 100644
> --- a/fs/nfsd/blocklayout.c
> +++ b/fs/nfsd/blocklayout.c
> @@ -179,15 +179,20 @@ static __be32
>  nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
>  		struct iomap *iomaps, int nr_iomaps)
>  {
> -	struct timespec64 mtime = inode_get_mtime(inode);
>  	struct iattr iattr = { .ia_valid = 0 };
>  	int error;
>  
> -	if (lcp->lc_mtime.tv_nsec == UTIME_NOW ||
> -	    timespec64_compare(&lcp->lc_mtime, &mtime) < 0)
> -		lcp->lc_mtime = current_time(inode);
> +	/*
> +	 * This ignores the client provided mtime in loca_time_modify, as a
> +	 * fully client specified mtime doesn't really fit into the Linux
> +	 * multi-grain timestamp architecture.
> +	 *
> +	 * RFC 8881 Section 18.42 makes it clear that the client provided
> +	 * timestamp is a "may" condition, and clients that want to force a
> +	 * specific timestamp should send a separate SETATTR in the compound.
> +	 */
>  	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
> -	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
> +	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = current_time(inode);

Technically, these fields are completely ignored by notify_change,
unless their counterpart ATTR_?TIME_SET flag is set, so you could just
leave them unset.
 
>  
>  	if (lcp->lc_size_chg) {
>  		iattr.ia_valid |= ATTR_SIZE;

Reviewed-by: Jeff Layton <jlayton@kernel.org>

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

* Re: [PATCH 2/4] exportfs: split out the ops for layout-based block device access
  2026-03-31 15:33 ` [PATCH 2/4] exportfs: split out the ops for layout-based block device access Christoph Hellwig
@ 2026-03-31 17:24   ` Jeff Layton
  2026-04-01 14:27     ` Christoph Hellwig
  2026-03-31 18:07   ` Chuck Lever
  1 sibling, 1 reply; 21+ messages in thread
From: Jeff Layton @ 2026-03-31 17:24 UTC (permalink / raw)
  To: Christoph Hellwig, Chuck Lever, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

On Tue, 2026-03-31 at 17:33 +0200, Christoph Hellwig wrote:
> The support to grant layouts for direct block device access works
> at a very different layer than the rest of exports.  Split the methods
> for it into a separate struct, and move that into a separate header
> to better split things out.  The pointer to the new operation vector
> is kept in export_operations to avoid bloating the super_block.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  MAINTAINERS                    |  2 +-
>  fs/nfsd/blocklayout.c          | 14 ++++++------
>  fs/nfsd/nfs4layouts.c          |  9 ++++----
>  fs/xfs/xfs_export.c            |  4 +---
>  fs/xfs/xfs_pnfs.c              | 12 ++++++++---
>  fs/xfs/xfs_pnfs.h              | 11 +++++-----
>  include/linux/exportfs.h       | 25 +++++++---------------
>  include/linux/exportfs_block.h | 39 ++++++++++++++++++++++++++++++++++
>  8 files changed, 74 insertions(+), 42 deletions(-)
>  create mode 100644 include/linux/exportfs_block.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c3fe46d7c4bc..b531b87a007e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9856,7 +9856,7 @@ S:	Supported
>  F:	Documentation/filesystems/nfs/exporting.rst
>  F:	fs/exportfs/
>  F:	fs/fhandle.c
> -F:	include/linux/exportfs.h
> +F:	include/linux/exportfs*.h
>  
>  FILESYSTEMS [IDMAPPED MOUNTS]
>  M:	Christian Brauner <brauner@kernel.org>
> diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> index 5ee5735b39bb..3cc3b47361e2 100644
> --- a/fs/nfsd/blocklayout.c
> +++ b/fs/nfsd/blocklayout.c
> @@ -2,7 +2,7 @@
>  /*
>   * Copyright (c) 2014-2016 Christoph Hellwig.
>   */
> -#include <linux/exportfs.h>
> +#include <linux/exportfs_block.h>
>  #include <linux/iomap.h>
>  #include <linux/slab.h>
>  #include <linux/pr.h>
> @@ -32,8 +32,8 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
>  	u32 device_generation = 0;
>  	int error;
>  
> -	error = sb->s_export_op->map_blocks(inode, offset, length, &iomap,
> -			iomode != IOMODE_READ, &device_generation);
> +	error = sb->s_export_op->block_ops->map_blocks(inode, offset, length,
> +			&iomap, iomode != IOMODE_READ, &device_generation);
>  	if (error) {
>  		if (error == -ENXIO)
>  			return nfserr_layoutunavailable;
> @@ -199,8 +199,8 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
>  		iattr.ia_size = lcp->lc_newsize;
>  	}
>  
> -	error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,
> -			nr_iomaps, &iattr);
> +	error = inode->i_sb->s_export_op->block_ops->commit_blocks(inode,
> +			iomaps, nr_iomaps, &iattr);
>  	kfree(iomaps);
>  	return nfserrno(error);
>  }
> @@ -223,8 +223,8 @@ nfsd4_block_get_device_info_simple(struct super_block *sb,
>  
>  	b->type = PNFS_BLOCK_VOLUME_SIMPLE;
>  	b->simple.sig_len = PNFS_BLOCK_UUID_LEN;
> -	return sb->s_export_op->get_uuid(sb, b->simple.sig, &b->simple.sig_len,
> -			&b->simple.offset);
> +	return sb->s_export_op->block_ops->get_uuid(sb, b->simple.sig,
> +			&b->simple.sig_len, &b->simple.offset);
>  }
>  
>  static __be32
> diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
> index ad7af8cfcf1f..c53eb67969eb 100644
> --- a/fs/nfsd/nfs4layouts.c
> +++ b/fs/nfsd/nfs4layouts.c
> @@ -3,6 +3,7 @@
>   * Copyright (c) 2014 Christoph Hellwig.
>   */
>  #include <linux/blkdev.h>
> +#include <linux/exportfs_block.h>
>  #include <linux/kmod.h>
>  #include <linux/file.h>
>  #include <linux/jhash.h>
> @@ -127,6 +128,7 @@ void nfsd4_setup_layout_type(struct svc_export *exp)
>  {
>  #if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
>  	struct super_block *sb = exp->ex_path.mnt->mnt_sb;
> +	struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
>  #endif
>  
>  	if (!(exp->ex_flags & NFSEXP_PNFS))
> @@ -136,14 +138,11 @@ void nfsd4_setup_layout_type(struct svc_export *exp)
>  	exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
>  #endif
>  #ifdef CONFIG_NFSD_BLOCKLAYOUT
> -	if (sb->s_export_op->get_uuid &&
> -	    sb->s_export_op->map_blocks &&
> -	    sb->s_export_op->commit_blocks)
> +	if (bops && bops->get_uuid && bops->map_blocks && bops->commit_blocks)
>  		exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
>  #endif
>  #ifdef CONFIG_NFSD_SCSILAYOUT
> -	if (sb->s_export_op->map_blocks &&
> -	    sb->s_export_op->commit_blocks &&
> +	if (bops && bops->map_blocks && bops->commit_blocks &&
>  	    sb->s_bdev &&
>  	    sb->s_bdev->bd_disk->fops->pr_ops &&
>  	    sb->s_bdev->bd_disk->fops->get_unique_id)
> diff --git a/fs/xfs/xfs_export.c b/fs/xfs/xfs_export.c
> index e3e3c3c89840..9b2ad3786b19 100644
> --- a/fs/xfs/xfs_export.c
> +++ b/fs/xfs/xfs_export.c
> @@ -244,8 +244,6 @@ const struct export_operations xfs_export_operations = {
>  	.get_parent		= xfs_fs_get_parent,
>  	.commit_metadata	= xfs_fs_nfs_commit_metadata,
>  #ifdef CONFIG_EXPORTFS_BLOCK_OPS
> -	.get_uuid		= xfs_fs_get_uuid,
> -	.map_blocks		= xfs_fs_map_blocks,
> -	.commit_blocks		= xfs_fs_commit_blocks,
> +	.block_ops		= &xfs_export_block_ops,
>  #endif
>  };
> diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
> index 221e55887a2a..a52978f6fb76 100644
> --- a/fs/xfs/xfs_pnfs.c
> +++ b/fs/xfs/xfs_pnfs.c
> @@ -49,7 +49,7 @@ xfs_break_leased_layouts(
>   * Get a unique ID including its location so that the client can identify
>   * the exported device.
>   */
> -int
> +static int
>  xfs_fs_get_uuid(
>  	struct super_block	*sb,
>  	u8			*buf,
> @@ -104,7 +104,7 @@ xfs_fs_map_update_inode(
>  /*
>   * Get a layout for the pNFS client.
>   */
> -int
> +static int
>  xfs_fs_map_blocks(
>  	struct inode		*inode,
>  	loff_t			offset,
> @@ -252,7 +252,7 @@ xfs_pnfs_validate_isize(
>   * to manually flush the cache here similar to what the fsync code path does
>   * for datasyncs on files that have no dirty metadata.
>   */
> -int
> +static int
>  xfs_fs_commit_blocks(
>  	struct inode		*inode,
>  	struct iomap		*maps,
> @@ -332,3 +332,9 @@ xfs_fs_commit_blocks(
>  	xfs_iunlock(ip, XFS_IOLOCK_EXCL);
>  	return error;
>  }
> +
> +struct exportfs_block_ops xfs_export_block_ops = {
> +	.get_uuid		= xfs_fs_get_uuid,
> +	.map_blocks		= xfs_fs_map_blocks,
> +	.commit_blocks		= xfs_fs_commit_blocks,
> +};
> diff --git a/fs/xfs/xfs_pnfs.h b/fs/xfs/xfs_pnfs.h
> index 940c6c2ad88c..9c289e98f2a6 100644
> --- a/fs/xfs/xfs_pnfs.h
> +++ b/fs/xfs/xfs_pnfs.h
> @@ -2,13 +2,9 @@
>  #ifndef _XFS_PNFS_H
>  #define _XFS_PNFS_H 1
>  
> -#ifdef CONFIG_EXPORTFS_BLOCK_OPS
> -int xfs_fs_get_uuid(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
> -int xfs_fs_map_blocks(struct inode *inode, loff_t offset, u64 length,
> -		struct iomap *iomap, bool write, u32 *device_generation);
> -int xfs_fs_commit_blocks(struct inode *inode, struct iomap *maps, int nr_maps,
> -		struct iattr *iattr);
> +#include <linux/exportfs_block.h>
>  
> +#ifdef CONFIG_EXPORTFS_BLOCK_OPS
>  int xfs_break_leased_layouts(struct inode *inode, uint *iolock,
>  		bool *did_unlock);
>  #else
> @@ -18,4 +14,7 @@ xfs_break_leased_layouts(struct inode *inode, uint *iolock, bool *did_unlock)
>  	return 0;
>  }
>  #endif /* CONFIG_EXPORTFS_BLOCK_OPS */
> +
> +extern struct exportfs_block_ops xfs_export_block_ops;
> +
>  #endif /* _XFS_PNFS_H */
> diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
> index 8bcdba28b406..f07ce833fba3 100644
> --- a/include/linux/exportfs.h
> +++ b/include/linux/exportfs.h
> @@ -6,9 +6,8 @@
>  #include <linux/path.h>
>  
>  struct dentry;
> -struct iattr;
> +struct exportfs_block_ops;
>  struct inode;
> -struct iomap;
>  struct super_block;
>  struct vfsmount;
>  
> @@ -260,19 +259,13 @@ struct handle_to_path_ctx {
>   * @commit_metadata:
>   *    @commit_metadata should commit metadata changes to stable storage.
>   *
> - * @get_uuid:
> - *    Get a filesystem unique signature exposed to clients.
> - *
> - * @map_blocks:
> - *    Map and, if necessary, allocate blocks for a layout.
> - *
> - * @commit_blocks:
> - *    Commit blocks in a layout once the client is done with them.
> - *
>   * @flags:
>   *    Allows the filesystem to communicate to nfsd that it may want to do things
>   *    differently when dealing with it.
>   *
> + * @block_ops:
> + *    Operations for layout grants to block on the underlying device.
> + *
>   * Locking rules:
>   *    get_parent is called with child->d_inode->i_rwsem down
>   *    get_name is not (which is possibly inconsistent)
> @@ -290,12 +283,6 @@ struct export_operations {
>  	struct dentry * (*get_parent)(struct dentry *child);
>  	int (*commit_metadata)(struct inode *inode);
>  
> -	int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
> -	int (*map_blocks)(struct inode *inode, loff_t offset,
> -			  u64 len, struct iomap *iomap,
> -			  bool write, u32 *device_generation);
> -	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
> -			     int nr_iomaps, struct iattr *iattr);
>  	int (*permission)(struct handle_to_path_ctx *ctx, unsigned int oflags);
>  	struct file * (*open)(const struct path *path, unsigned int oflags);
>  #define	EXPORT_OP_NOWCC			(0x1) /* don't collect v3 wcc data */
> @@ -308,6 +295,10 @@ struct export_operations {
>  #define EXPORT_OP_FLUSH_ON_CLOSE	(0x20) /* fs flushes file data on close */
>  #define EXPORT_OP_NOLOCKS		(0x40) /* no file locking support */
>  	unsigned long	flags;
> +
> +#ifdef CONFIG_EXPORTFS_BLOCK_OPS
> +	struct exportfs_block_ops *block_ops;
> +#endif
>  };
>  
>  /**
> diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
> new file mode 100644
> index 000000000000..1f52fea8e4dc
> --- /dev/null
> +++ b/include/linux/exportfs_block.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2014-2026 Christoph Hellwig.
> + *
> + * Support for exportfs-based layout grants for direct block device access.
> + */
> +#ifndef LINUX_EXPORTFS_BLOCK_H
> +#define LINUX_EXPORTFS_BLOCK_H 1
> +
> +#include <linux/types.h>
> +
> +struct iattr;
> +struct inode;
> +struct iomap;
> +struct super_block;
> +
> +struct exportfs_block_ops {
> +	/*
> +	 * Get the in-band device unique signature exposed to clients.
> +	 */
> +	int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
> +
> +	/*
> +	 * Map blocks for direct block access.
> +	 * If @write is %true, also allocate the blocks for the range if needed.
> +	 */
> +	int (*map_blocks)(struct inode *inode, loff_t offset, u64 len,
> +			struct iomap *iomap, bool write,
> +			u32 *device_generation);
> +
> +	/*
> +	 * Commit blocks previously handed out by ->map_blocks and written to by
> +	 * the client.
> +	 */
> +	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
> +			int nr_iomaps, struct iattr *iattr);
> +};
> +
> +#endif /* LINUX_EXPORTFS_BLOCK_H */

Meh, ok. Almost doesn't seem worthwhile, but I guess the result is a
bit cleaner layering-wise.

Reviewed-by: Jeff Layton <jlayton@kernel.org>

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

* Re: [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks
  2026-03-31 15:33 ` [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks Christoph Hellwig
@ 2026-03-31 17:26   ` Jeff Layton
  2026-03-31 17:30     ` Chuck Lever
  0 siblings, 1 reply; 21+ messages in thread
From: Jeff Layton @ 2026-03-31 17:26 UTC (permalink / raw)
  To: Christoph Hellwig, Chuck Lever, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

On Tue, 2026-03-31 at 17:33 +0200, Christoph Hellwig wrote:
> The only thing ->commit_blocks really needs is the new size, with a magic
> -1 placeholder 0 for "do not change the size" because it only ever
> extends the size.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/nfsd/blocklayout.c          | 12 ++----------
>  fs/xfs/xfs_pnfs.c              | 19 ++++++++++---------
>  include/linux/exportfs_block.h |  3 +--
>  3 files changed, 13 insertions(+), 21 deletions(-)
> 
> diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> index 3cc3b47361e2..23c0e4d0ff34 100644
> --- a/fs/nfsd/blocklayout.c
> +++ b/fs/nfsd/blocklayout.c
> @@ -179,7 +179,6 @@ static __be32
>  nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
>  		struct iomap *iomaps, int nr_iomaps)
>  {
> -	struct iattr iattr = { .ia_valid = 0 };
>  	int error;
>  
>  	/*
> @@ -191,16 +190,9 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
>  	 * timestamp is a "may" condition, and clients that want to force a
>  	 * specific timestamp should send a separate SETATTR in the compound.
>  	 */
> -	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
> -	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = current_time(inode);
> -
> -	if (lcp->lc_size_chg) {
> -		iattr.ia_valid |= ATTR_SIZE;
> -		iattr.ia_size = lcp->lc_newsize;
> -	}
> -
>  	error = inode->i_sb->s_export_op->block_ops->commit_blocks(inode,
> -			iomaps, nr_iomaps, &iattr);
> +			iomaps, nr_iomaps,
> +			lcp->lc_size_chg ? lcp->lc_newsize : 0); 
>  	kfree(iomaps);
>  	return nfserrno(error);
>  }
> diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
> index a52978f6fb76..fee782a3edbe 100644
> --- a/fs/xfs/xfs_pnfs.c
> +++ b/fs/xfs/xfs_pnfs.c
> @@ -257,23 +257,22 @@ xfs_fs_commit_blocks(
>  	struct inode		*inode,
>  	struct iomap		*maps,
>  	int			nr_maps,
> -	struct iattr		*iattr)
> +	loff_t			new_size)
>  {
>  	struct xfs_inode	*ip = XFS_I(inode);
>  	struct xfs_mount	*mp = ip->i_mount;
>  	struct xfs_trans	*tp;
> +	struct timespec64	now;
>  	bool			update_isize = false;
>  	int			error, i;
>  	loff_t			size;
>  
> -	ASSERT(iattr->ia_valid & (ATTR_ATIME|ATTR_CTIME|ATTR_MTIME));
> -
>  	xfs_ilock(ip, XFS_IOLOCK_EXCL);
>  
>  	size = i_size_read(inode);
> -	if ((iattr->ia_valid & ATTR_SIZE) && iattr->ia_size > size) {
> +	if (new_size > size) {
>  		update_isize = true;
> -		size = iattr->ia_size;
> +		size = new_size;
>  	}
>  
>  	for (i = 0; i < nr_maps; i++) {
> @@ -318,11 +317,13 @@ xfs_fs_commit_blocks(
>  	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
>  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
>  
> -	ASSERT(!(iattr->ia_valid & (ATTR_UID | ATTR_GID)));
> -	setattr_copy(&nop_mnt_idmap, inode, iattr);
> +	now = inode_set_ctime_current(inode);
> +	inode_set_atime_to_ts(inode, now);
> +	inode_set_mtime_to_ts(inode, now);
> +
>  	if (update_isize) {
> -		i_size_write(inode, iattr->ia_size);
> -		ip->i_disk_size = iattr->ia_size;
> +		i_size_write(inode, new_size);
> +		ip->i_disk_size = new_size;
>  	}
>  
>  	xfs_trans_set_sync(tp);
> diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
> index 1f52fea8e4dc..d1dec4689b14 100644
> --- a/include/linux/exportfs_block.h
> +++ b/include/linux/exportfs_block.h
> @@ -9,7 +9,6 @@
>  
>  #include <linux/types.h>
>  
> -struct iattr;
>  struct inode;
>  struct iomap;
>  struct super_block;
> @@ -33,7 +32,7 @@ struct exportfs_block_ops {
>  	 * the client.
>  	 */
>  	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
> -			int nr_iomaps, struct iattr *iattr);
> +			int nr_iomaps, loff_t new_size);
>  };
>  
>  #endif /* LINUX_EXPORTFS_BLOCK_H */

I like this one. I think you can just fold the first patch in this
series into this one, since you're making that change moot (and the
ia_* times don't matter much anyway).

Reviewed-by: Jeff Layton <jlayton@kernel.org>

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

* Re: [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks
  2026-03-31 17:26   ` Jeff Layton
@ 2026-03-31 17:30     ` Chuck Lever
  2026-03-31 17:33       ` Jeff Layton
  0 siblings, 1 reply; 21+ messages in thread
From: Chuck Lever @ 2026-03-31 17:30 UTC (permalink / raw)
  To: Jeff Layton, Christoph Hellwig, Chuck Lever, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel



On Tue, Mar 31, 2026, at 1:26 PM, Jeff Layton wrote:
> On Tue, 2026-03-31 at 17:33 +0200, Christoph Hellwig wrote:
>> The only thing ->commit_blocks really needs is the new size, with a magic
>> -1 placeholder 0 for "do not change the size" because it only ever
>> extends the size.
>> 
>> Signed-off-by: Christoph Hellwig <hch@lst.de>
>> ---
>>  fs/nfsd/blocklayout.c          | 12 ++----------
>>  fs/xfs/xfs_pnfs.c              | 19 ++++++++++---------
>>  include/linux/exportfs_block.h |  3 +--
>>  3 files changed, 13 insertions(+), 21 deletions(-)
>> 
>> diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
>> index 3cc3b47361e2..23c0e4d0ff34 100644
>> --- a/fs/nfsd/blocklayout.c
>> +++ b/fs/nfsd/blocklayout.c
>> @@ -179,7 +179,6 @@ static __be32
>>  nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
>>  		struct iomap *iomaps, int nr_iomaps)
>>  {
>> -	struct iattr iattr = { .ia_valid = 0 };
>>  	int error;
>>  
>>  	/*
>> @@ -191,16 +190,9 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
>>  	 * timestamp is a "may" condition, and clients that want to force a
>>  	 * specific timestamp should send a separate SETATTR in the compound.
>>  	 */
>> -	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
>> -	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = current_time(inode);
>> -
>> -	if (lcp->lc_size_chg) {
>> -		iattr.ia_valid |= ATTR_SIZE;
>> -		iattr.ia_size = lcp->lc_newsize;
>> -	}
>> -
>>  	error = inode->i_sb->s_export_op->block_ops->commit_blocks(inode,
>> -			iomaps, nr_iomaps, &iattr);
>> +			iomaps, nr_iomaps,
>> +			lcp->lc_size_chg ? lcp->lc_newsize : 0); 
>>  	kfree(iomaps);
>>  	return nfserrno(error);
>>  }
>> diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
>> index a52978f6fb76..fee782a3edbe 100644
>> --- a/fs/xfs/xfs_pnfs.c
>> +++ b/fs/xfs/xfs_pnfs.c
>> @@ -257,23 +257,22 @@ xfs_fs_commit_blocks(
>>  	struct inode		*inode,
>>  	struct iomap		*maps,
>>  	int			nr_maps,
>> -	struct iattr		*iattr)
>> +	loff_t			new_size)
>>  {
>>  	struct xfs_inode	*ip = XFS_I(inode);
>>  	struct xfs_mount	*mp = ip->i_mount;
>>  	struct xfs_trans	*tp;
>> +	struct timespec64	now;
>>  	bool			update_isize = false;
>>  	int			error, i;
>>  	loff_t			size;
>>  
>> -	ASSERT(iattr->ia_valid & (ATTR_ATIME|ATTR_CTIME|ATTR_MTIME));
>> -
>>  	xfs_ilock(ip, XFS_IOLOCK_EXCL);
>>  
>>  	size = i_size_read(inode);
>> -	if ((iattr->ia_valid & ATTR_SIZE) && iattr->ia_size > size) {
>> +	if (new_size > size) {
>>  		update_isize = true;
>> -		size = iattr->ia_size;
>> +		size = new_size;
>>  	}
>>  
>>  	for (i = 0; i < nr_maps; i++) {
>> @@ -318,11 +317,13 @@ xfs_fs_commit_blocks(
>>  	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
>>  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
>>  
>> -	ASSERT(!(iattr->ia_valid & (ATTR_UID | ATTR_GID)));
>> -	setattr_copy(&nop_mnt_idmap, inode, iattr);
>> +	now = inode_set_ctime_current(inode);
>> +	inode_set_atime_to_ts(inode, now);
>> +	inode_set_mtime_to_ts(inode, now);
>> +
>>  	if (update_isize) {
>> -		i_size_write(inode, iattr->ia_size);
>> -		ip->i_disk_size = iattr->ia_size;
>> +		i_size_write(inode, new_size);
>> +		ip->i_disk_size = new_size;
>>  	}
>>  
>>  	xfs_trans_set_sync(tp);
>> diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
>> index 1f52fea8e4dc..d1dec4689b14 100644
>> --- a/include/linux/exportfs_block.h
>> +++ b/include/linux/exportfs_block.h
>> @@ -9,7 +9,6 @@
>>  
>>  #include <linux/types.h>
>>  
>> -struct iattr;
>>  struct inode;
>>  struct iomap;
>>  struct super_block;
>> @@ -33,7 +32,7 @@ struct exportfs_block_ops {
>>  	 * the client.
>>  	 */
>>  	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
>> -			int nr_iomaps, struct iattr *iattr);
>> +			int nr_iomaps, loff_t new_size);
>>  };
>>  
>>  #endif /* LINUX_EXPORTFS_BLOCK_H */
>
> I like this one. I think you can just fold the first patch in this
> series into this one

Disagree: I think the change in the first patch needs its own
patch description and motivation.

> since you're making that change moot (and the
> ia_* times don't matter much anyway).
>
> Reviewed-by: Jeff Layton <jlayton@kernel.org>

-- 
Chuck Lever

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

* Re: [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks
  2026-03-31 17:30     ` Chuck Lever
@ 2026-03-31 17:33       ` Jeff Layton
  2026-04-01  6:24         ` Christoph Hellwig
  0 siblings, 1 reply; 21+ messages in thread
From: Jeff Layton @ 2026-03-31 17:33 UTC (permalink / raw)
  To: Chuck Lever, Christoph Hellwig, Chuck Lever, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

On Tue, 2026-03-31 at 13:30 -0400, Chuck Lever wrote:
> 
> On Tue, Mar 31, 2026, at 1:26 PM, Jeff Layton wrote:
> > On Tue, 2026-03-31 at 17:33 +0200, Christoph Hellwig wrote:
> > > The only thing ->commit_blocks really needs is the new size, with a magic
> > > -1 placeholder 0 for "do not change the size" because it only ever
> > > extends the size.
> > > 
> > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > ---
> > >  fs/nfsd/blocklayout.c          | 12 ++----------
> > >  fs/xfs/xfs_pnfs.c              | 19 ++++++++++---------
> > >  include/linux/exportfs_block.h |  3 +--
> > >  3 files changed, 13 insertions(+), 21 deletions(-)
> > > 
> > > diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> > > index 3cc3b47361e2..23c0e4d0ff34 100644
> > > --- a/fs/nfsd/blocklayout.c
> > > +++ b/fs/nfsd/blocklayout.c
> > > @@ -179,7 +179,6 @@ static __be32
> > >  nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
> > >  		struct iomap *iomaps, int nr_iomaps)
> > >  {
> > > -	struct iattr iattr = { .ia_valid = 0 };
> > >  	int error;
> > >  
> > >  	/*
> > > @@ -191,16 +190,9 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
> > >  	 * timestamp is a "may" condition, and clients that want to force a
> > >  	 * specific timestamp should send a separate SETATTR in the compound.
> > >  	 */
> > > -	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
> > > -	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = current_time(inode);
> > > -
> > > -	if (lcp->lc_size_chg) {
> > > -		iattr.ia_valid |= ATTR_SIZE;
> > > -		iattr.ia_size = lcp->lc_newsize;
> > > -	}
> > > -

[...]

> > 
> > I like this one. I think you can just fold the first patch in this
> > series into this one
> 
> Disagree: I think the change in the first patch needs its own
> patch description and motivation.
>
> > since you're making that change moot (and the
> > ia_* times don't matter much anyway).
> > 

Your call, but note that the changes in the first patch are completely
removed by this one. There's really no need for that patch if you take
this one too.

-- 
Jeff Layton <jlayton@kernel.org>

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

* Re: cleanup block-style layouts exports
  2026-03-31 15:33 cleanup block-style layouts exports Christoph Hellwig
                   ` (3 preceding siblings ...)
  2026-03-31 15:33 ` [PATCH 4/4] exportfs,nfsd: rework checking for layout-based block device access support Christoph Hellwig
@ 2026-03-31 17:33 ` Chuck Lever
  2026-04-01  6:19   ` Christoph Hellwig
  4 siblings, 1 reply; 21+ messages in thread
From: Chuck Lever @ 2026-03-31 17:33 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel, Chuck Lever, Jeff Layton, Amir Goldstein


On Tue, Mar 31, 2026, at 11:33 AM, Christoph Hellwig wrote:
> Hi all,
>
> this series cleanups the exportfs support for block-style layouts that
> provide direct block device access.  This is preparation for supporting
> exportfs of more than a single device per file system.
>
> Changes since the multi-device export series:
>  - check for NULL bops in nfsd4_setup_layout_type
>  - clearly document why we are ignoring loca_time_modify
>
> Diffstat:
>  MAINTAINERS                    |    2 
>  fs/nfsd/blocklayout.c          |   37 +++++++----------
>  fs/nfsd/export.c               |    3 -
>  fs/nfsd/nfs4layouts.c          |   29 +++----------
>  fs/xfs/xfs_export.c            |    4 -
>  fs/xfs/xfs_pnfs.c              |   44 ++++++++++++++------
>  fs/xfs/xfs_pnfs.h              |   11 ++---
>  include/linux/exportfs.h       |   25 +++--------
>  include/linux/exportfs_block.h |   88 +++++++++++++++++++++++++++++++++++++++++
>  9 files changed, 162 insertions(+), 81 deletions(-)

How do you see this getting merged? Do you want all of these
patches to go through the NFS tree? Or do you want this to
go through VFS?


-- 
Chuck Lever

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

* Re: [PATCH 4/4] exportfs,nfsd: rework checking for layout-based block device access support
  2026-03-31 15:33 ` [PATCH 4/4] exportfs,nfsd: rework checking for layout-based block device access support Christoph Hellwig
@ 2026-03-31 17:52   ` Jeff Layton
  2026-03-31 18:00   ` Chuck Lever
  1 sibling, 0 replies; 21+ messages in thread
From: Jeff Layton @ 2026-03-31 17:52 UTC (permalink / raw)
  To: Christoph Hellwig, Chuck Lever, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

On Tue, 2026-03-31 at 17:33 +0200, Christoph Hellwig wrote:
> Currently NFSD hard codes checking support for block-style layouts.
> Lift the checks into a file system-helper and provide a exportfs-level
> helper to implement the typical checks.
> 
> This prepares for supporting block layout export of multiple devices
> per file system.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/nfsd/export.c               |  3 +-
>  fs/nfsd/nfs4layouts.c          | 26 +++++------------
>  fs/xfs/xfs_pnfs.c              | 13 +++++++++
>  include/linux/exportfs_block.h | 52 +++++++++++++++++++++++++++++++++-
>  4 files changed, 73 insertions(+), 21 deletions(-)
> 
> diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
> index 8e8a76a44ff0..e20298f9212f 100644
> --- a/fs/nfsd/export.c
> +++ b/fs/nfsd/export.c
> @@ -735,7 +735,8 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
>  			goto out4;
>  		err = 0;
>  
> -		nfsd4_setup_layout_type(&exp);
> +		if (exp.ex_flags & NFSEXP_PNFS)
> +			nfsd4_setup_layout_type(&exp);
>  	}
>  
>  	expp = svc_export_lookup(&exp);
> diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
> index c53eb67969eb..7b849b637b5e 100644
> --- a/fs/nfsd/nfs4layouts.c
> +++ b/fs/nfsd/nfs4layouts.c
> @@ -2,7 +2,6 @@
>  /*
>   * Copyright (c) 2014 Christoph Hellwig.
>   */
> -#include <linux/blkdev.h>
>  #include <linux/exportfs_block.h>
>  #include <linux/kmod.h>
>  #include <linux/file.h>
> @@ -126,28 +125,17 @@ nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
>  
>  void nfsd4_setup_layout_type(struct svc_export *exp)
>  {
> -#if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
>  	struct super_block *sb = exp->ex_path.mnt->mnt_sb;
> -	struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
> -#endif
> -
> -	if (!(exp->ex_flags & NFSEXP_PNFS))
> -		return;
> +	expfs_block_layouts_t block_supported = exporfs_layouts_supported(sb);
>  
> -#ifdef CONFIG_NFSD_FLEXFILELAYOUT
> -	exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
> -#endif
> -#ifdef CONFIG_NFSD_BLOCKLAYOUT
> -	if (bops && bops->get_uuid && bops->map_blocks && bops->commit_blocks)
> +	if (IS_ENABLED(CONFIG_NFSD_FLEXFILELAYOUT))
> +		exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
> +	if (IS_ENABLED(CONFIG_NFSD_BLOCKLAYOUT) &&
> +	    (block_supported & EXPFS_BLOCK_IN_BAND_ID))
>  		exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
> -#endif
> -#ifdef CONFIG_NFSD_SCSILAYOUT
> -	if (bops && bops->map_blocks && bops->commit_blocks &&
> -	    sb->s_bdev &&
> -	    sb->s_bdev->bd_disk->fops->pr_ops &&
> -	    sb->s_bdev->bd_disk->fops->get_unique_id)
> +	if (IS_ENABLED(CONFIG_NFSD_SCSILAYOUT) &&
> +	    (block_supported & EXPFS_BLOCK_OUT_OF_BAND_ID))
>  		exp->ex_layout_types |= 1 << LAYOUT_SCSI;
> -#endif
>  }
>  
>  void nfsd4_close_layout(struct nfs4_layout_stateid *ls)
> diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
> index fee782a3edbe..acefa0b99f53 100644
> --- a/fs/xfs/xfs_pnfs.c
> +++ b/fs/xfs/xfs_pnfs.c
> @@ -13,6 +13,7 @@
>  #include "xfs_bmap.h"
>  #include "xfs_iomap.h"
>  #include "xfs_pnfs.h"
> +#include <linux/exportfs_block.h>
>  
>  /*
>   * Ensure that we do not have any outstanding pNFS layouts that can be used by
> @@ -45,6 +46,17 @@ xfs_break_leased_layouts(
>  	return error;
>  }
>  
> +static expfs_block_layouts_t
> +xfs_fs_layouts_supported(
> +	struct super_block	*sb)
> +{
> +	expfs_block_layouts_t	supported = EXPFS_BLOCK_IN_BAND_ID;
> +
> +	if (exportfs_bdev_supports_out_of_band_id(sb->s_bdev))
> +		supported |= EXPFS_BLOCK_OUT_OF_BAND_ID;
> +	return supported;
> +}
> +
>  /*
>   * Get a unique ID including its location so that the client can identify
>   * the exported device.
> @@ -335,6 +347,7 @@ xfs_fs_commit_blocks(
>  }
>  
>  struct exportfs_block_ops xfs_export_block_ops = {
> +	.layouts_supported	= xfs_fs_layouts_supported,
>  	.get_uuid		= xfs_fs_get_uuid,
>  	.map_blocks		= xfs_fs_map_blocks,
>  	.commit_blocks		= xfs_fs_commit_blocks,
> diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
> index d1dec4689b14..8d5b0b0c5a82 100644
> --- a/include/linux/exportfs_block.h
> +++ b/include/linux/exportfs_block.h
> @@ -7,13 +7,35 @@
>  #ifndef LINUX_EXPORTFS_BLOCK_H
>  #define LINUX_EXPORTFS_BLOCK_H 1
>  
> -#include <linux/types.h>
> +#include <linux/blkdev.h>
> +#include <linux/exportfs.h>
> +#include <linux/fs.h>
>  
>  struct inode;
>  struct iomap;
>  struct super_block;
>  
> +/*
> + * There are the two types of block-style layout support:
> + *  - In-band implies a device identified by a unique cookie inside the actual
> + *    device address space checked by the ->get_uuid method as used by the pNFS
> + *    block layout.  This is a bit dangerous and deprecated.
> + *  - Out of band implies identification by out of band unique identifiers
> + *    specified by the storage protocol, which is much safer and used by the
> + *    pNFS SCSI/NVMe layouts.
> + */
> +typedef unsigned int __bitwise expfs_block_layouts_t;
> +#define EXPFS_BLOCK_FLAG(__bit) \
> +	((__force expfs_block_layouts_t)(1u << __bit))
> +#define EXPFS_BLOCK_IN_BAND_ID		EXPFS_BLOCK_FLAG(0)
> +#define EXPFS_BLOCK_OUT_OF_BAND_ID	EXPFS_BLOCK_FLAG(1)
> +
>  struct exportfs_block_ops {
> +	/*
> +	 * Returns the EXPFS_BLOCK_* bitmap of supported layout types.
> +	 */
> +	expfs_block_layouts_t (*layouts_supported)(struct super_block *sb);
> +
>  	/*
>  	 * Get the in-band device unique signature exposed to clients.
>  	 */
> @@ -35,4 +57,32 @@ struct exportfs_block_ops {
>  			int nr_iomaps, loff_t new_size);
>  };
>  
> +static inline bool
> +exportfs_bdev_supports_out_of_band_id(struct block_device *bdev)
> +{
> +	return bdev->bd_disk->fops->pr_ops &&
> +		bdev->bd_disk->fops->get_unique_id;
> +}
> +
> +#ifdef CONFIG_EXPORTFS_BLOCK_OPS
> +static inline expfs_block_layouts_t
> +exporfs_layouts_supported(struct super_block *sb)
> +{
> +	struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
> +
> +	if (!bops ||
> +	    !bops->layouts_supported ||
> +	    WARN_ON_ONCE(!bops->map_blocks) ||
> +	    WARN_ON_ONCE(!bops->commit_blocks))
> +		return 0;
> +	return bops->layouts_supported(sb);
> +}
> +#else
> +static inline expfs_block_layouts_t
> +exporfs_layouts_supported(struct super_block *sb)
> +{
> +	return 0;
> +}
> +#endif /* CONFIG_EXPORTFS_BLOCK_OPS */
> +
>  #endif /* LINUX_EXPORTFS_BLOCK_H */

Much cleaner.

Reviewed-by: Jeff Layton <jlayton@kernel.org>

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

* Re: [PATCH 4/4] exportfs,nfsd: rework checking for layout-based block device access support
  2026-03-31 15:33 ` [PATCH 4/4] exportfs,nfsd: rework checking for layout-based block device access support Christoph Hellwig
  2026-03-31 17:52   ` Jeff Layton
@ 2026-03-31 18:00   ` Chuck Lever
  1 sibling, 0 replies; 21+ messages in thread
From: Chuck Lever @ 2026-03-31 18:00 UTC (permalink / raw)
  To: Christoph Hellwig, Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel



On Tue, Mar 31, 2026, at 11:33 AM, Christoph Hellwig wrote:

> diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
> index c53eb67969eb..7b849b637b5e 100644
> --- a/fs/nfsd/nfs4layouts.c
> +++ b/fs/nfsd/nfs4layouts.c
> @@ -2,7 +2,6 @@
>  /*
>   * Copyright (c) 2014 Christoph Hellwig.
>   */
> -#include <linux/blkdev.h>
>  #include <linux/exportfs_block.h>
>  #include <linux/kmod.h>
>  #include <linux/file.h>
> @@ -126,28 +125,17 @@ nfsd4_set_deviceid(struct nfsd4_deviceid *id, 
> const struct svc_fh *fhp,
> 
>  void nfsd4_setup_layout_type(struct svc_export *exp)
>  {
> -#if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
>  	struct super_block *sb = exp->ex_path.mnt->mnt_sb;
> -	struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
> -#endif
> -
> -	if (!(exp->ex_flags & NFSEXP_PNFS))
> -		return;
> +	expfs_block_layouts_t block_supported = exporfs_layouts_supported(sb);

^exporfs^exportfs

throughout the patch.


> -#ifdef CONFIG_NFSD_FLEXFILELAYOUT
> -	exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
> -#endif
> -#ifdef CONFIG_NFSD_BLOCKLAYOUT
> -	if (bops && bops->get_uuid && bops->map_blocks && bops->commit_blocks)
> +	if (IS_ENABLED(CONFIG_NFSD_FLEXFILELAYOUT))
> +		exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
> +	if (IS_ENABLED(CONFIG_NFSD_BLOCKLAYOUT) &&
> +	    (block_supported & EXPFS_BLOCK_IN_BAND_ID))
>  		exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
> -#endif
> -#ifdef CONFIG_NFSD_SCSILAYOUT
> -	if (bops && bops->map_blocks && bops->commit_blocks &&
> -	    sb->s_bdev &&
> -	    sb->s_bdev->bd_disk->fops->pr_ops &&
> -	    sb->s_bdev->bd_disk->fops->get_unique_id)
> +	if (IS_ENABLED(CONFIG_NFSD_SCSILAYOUT) &&
> +	    (block_supported & EXPFS_BLOCK_OUT_OF_BAND_ID))
>  		exp->ex_layout_types |= 1 << LAYOUT_SCSI;
> -#endif
>  }
> 
>  void nfsd4_close_layout(struct nfs4_layout_stateid *ls)


-- 
Chuck Lever

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

* Re: [PATCH 2/4] exportfs: split out the ops for layout-based block device access
  2026-03-31 15:33 ` [PATCH 2/4] exportfs: split out the ops for layout-based block device access Christoph Hellwig
  2026-03-31 17:24   ` Jeff Layton
@ 2026-03-31 18:07   ` Chuck Lever
  2026-04-01  6:23     ` Christoph Hellwig
  1 sibling, 1 reply; 21+ messages in thread
From: Chuck Lever @ 2026-03-31 18:07 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel, Chuck Lever, Jeff Layton, Amir Goldstein


On Tue, Mar 31, 2026, at 11:33 AM, Christoph Hellwig wrote:
> The support to grant layouts for direct block device access works
> at a very different layer than the rest of exports.  Split the methods
> for it into a separate struct, and move that into a separate header
> to better split things out.  The pointer to the new operation vector
> is kept in export_operations to avoid bloating the super_block.

> diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
> index 221e55887a2a..a52978f6fb76 100644
> --- a/fs/xfs/xfs_pnfs.c
> +++ b/fs/xfs/xfs_pnfs.c
> @@ -332,3 +332,9 @@ xfs_fs_commit_blocks(
>  	xfs_iunlock(ip, XFS_IOLOCK_EXCL);
>  	return error;
>  }
> +
> +struct exportfs_block_ops xfs_export_block_ops = {
> +	.get_uuid		= xfs_fs_get_uuid,
> +	.map_blocks		= xfs_fs_map_blocks,
> +	.commit_blocks		= xfs_fs_commit_blocks,
> +};

Should xfs_export_block_ops be declared "const" ?


-- 
Chuck Lever

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

* Re: cleanup block-style layouts exports
  2026-03-31 17:33 ` cleanup block-style layouts exports Chuck Lever
@ 2026-04-01  6:19   ` Christoph Hellwig
  2026-04-01 13:39     ` Chuck Lever
  0 siblings, 1 reply; 21+ messages in thread
From: Christoph Hellwig @ 2026-04-01  6:19 UTC (permalink / raw)
  To: Chuck Lever
  Cc: Christoph Hellwig, NeilBrown, Olga Kornievskaia, Dai Ngo,
	Tom Talpey, linux-nfs, linux-fsdevel, Chuck Lever, Jeff Layton,
	Amir Goldstein

On Tue, Mar 31, 2026 at 01:33:11PM -0400, Chuck Lever wrote:
> 
> How do you see this getting merged? Do you want all of these
> patches to go through the NFS tree? Or do you want this to
> go through VFS?

I don't really care.  But I traditionally see exportfs as a nfsd domain,
even despite the added other uses of it.


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

* Re: [PATCH 2/4] exportfs: split out the ops for layout-based block device access
  2026-03-31 18:07   ` Chuck Lever
@ 2026-04-01  6:23     ` Christoph Hellwig
  0 siblings, 0 replies; 21+ messages in thread
From: Christoph Hellwig @ 2026-04-01  6:23 UTC (permalink / raw)
  To: Chuck Lever
  Cc: Christoph Hellwig, NeilBrown, Olga Kornievskaia, Dai Ngo,
	Tom Talpey, linux-nfs, linux-fsdevel, Chuck Lever, Jeff Layton,
	Amir Goldstein

On Tue, Mar 31, 2026 at 02:07:12PM -0400, Chuck Lever wrote:
> > +struct exportfs_block_ops xfs_export_block_ops = {
> > +	.get_uuid		= xfs_fs_get_uuid,
> > +	.map_blocks		= xfs_fs_map_blocks,
> > +	.commit_blocks		= xfs_fs_commit_blocks,
> > +};
> 
> Should xfs_export_block_ops be declared "const" ?

Probably.  If something doesn't support that right now I'll need to fix
it.


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

* Re: [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks
  2026-03-31 17:33       ` Jeff Layton
@ 2026-04-01  6:24         ` Christoph Hellwig
  0 siblings, 0 replies; 21+ messages in thread
From: Christoph Hellwig @ 2026-04-01  6:24 UTC (permalink / raw)
  To: Jeff Layton
  Cc: Chuck Lever, Christoph Hellwig, Chuck Lever, Amir Goldstein,
	NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

On Tue, Mar 31, 2026 at 01:33:10PM -0400, Jeff Layton wrote:
> > Disagree: I think the change in the first patch needs its own
> > patch description and motivation.
> >
> > > since you're making that change moot (and the
> > > ia_* times don't matter much anyway).
> > > 
> 
> Your call, but note that the changes in the first patch are completely
> removed by this one. There's really no need for that patch if you take
> this one too.

The big fat comment is still left.  The first patch is mostly intended as
clearly documenting what we're doing.  There is no behavior except for
sampling the current time more often in it anyway.


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

* Re: cleanup block-style layouts exports
  2026-04-01  6:19   ` Christoph Hellwig
@ 2026-04-01 13:39     ` Chuck Lever
  0 siblings, 0 replies; 21+ messages in thread
From: Chuck Lever @ 2026-04-01 13:39 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel, Chuck Lever, Jeff Layton, Amir Goldstein


On Wed, Apr 1, 2026, at 2:19 AM, Christoph Hellwig wrote:
> On Tue, Mar 31, 2026 at 01:33:11PM -0400, Chuck Lever wrote:
>> 
>> How do you see this getting merged? Do you want all of these
>> patches to go through the NFS tree? Or do you want this to
>> go through VFS?
>
> I don't really care.  But I traditionally see exportfs as a nfsd domain,
> even despite the added other uses of it.

Right, and I'm happy to take this series because it appears directly
related to pNFS. But recent exportfs changes have gone through one
of Christian's trees. Plus this is a cross-subsystem change.

(I'm not pushing back, I'd just like an above-board decision)

-- 
Chuck Lever

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

* Re: [PATCH 1/4] nfsd/blocklayout: always ignore loca_time_modify
  2026-03-31 17:09   ` Jeff Layton
@ 2026-04-01 14:25     ` Christoph Hellwig
  0 siblings, 0 replies; 21+ messages in thread
From: Christoph Hellwig @ 2026-04-01 14:25 UTC (permalink / raw)
  To: Jeff Layton
  Cc: Christoph Hellwig, Chuck Lever, Amir Goldstein, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs, linux-fsdevel

On Tue, Mar 31, 2026 at 01:09:28PM -0400, Jeff Layton wrote:
> >  	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
> > -	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
> > +	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = current_time(inode);
> 
> Technically, these fields are completely ignored by notify_change,
> unless their counterpart ATTR_?TIME_SET flag is set, so you could just
> leave them unset.

We're not actually going through notify_change here, so for now we'll
need this assignment for non-mgtime file systems.

Btw, shouldn't we move the actual time sampling from notify_change into
->setattr for non-mgtime file systems to avoid the extra sampling in
notify_change for the mgtime fast path?


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

* Re: [PATCH 2/4] exportfs: split out the ops for layout-based block device access
  2026-03-31 17:24   ` Jeff Layton
@ 2026-04-01 14:27     ` Christoph Hellwig
  0 siblings, 0 replies; 21+ messages in thread
From: Christoph Hellwig @ 2026-04-01 14:27 UTC (permalink / raw)
  To: Jeff Layton
  Cc: Christoph Hellwig, Chuck Lever, Amir Goldstein, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs, linux-fsdevel

On Tue, Mar 31, 2026 at 01:24:23PM -0400, Jeff Layton wrote:
> Meh, ok. Almost doesn't seem worthwhile, but I guess the result is a
> bit cleaner layering-wise.

It also avoid pulling extra includes into exportfs.h only needed for
the new block layout inline helpers added in the last patch.


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

* [PATCH 2/4] exportfs: split out the ops for layout-based block device access
  2026-04-01 14:40 cleanup block-style layouts exports v2 Christoph Hellwig
@ 2026-04-01 14:40 ` Christoph Hellwig
  0 siblings, 0 replies; 21+ messages in thread
From: Christoph Hellwig @ 2026-04-01 14:40 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs,
	linux-fsdevel

The support to grant layouts for direct block device access works
at a very different layer than the rest of exports.  Split the methods
for it into a separate struct, and move that into a separate header
to better split things out.  The pointer to the new operation vector
is kept in export_operations to avoid bloating the super_block.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
---
 MAINTAINERS                    |  2 +-
 fs/nfsd/blocklayout.c          | 14 ++++++------
 fs/nfsd/nfs4layouts.c          |  9 ++++----
 fs/xfs/xfs_export.c            |  4 +---
 fs/xfs/xfs_pnfs.c              | 12 ++++++++---
 fs/xfs/xfs_pnfs.h              | 11 +++++-----
 include/linux/exportfs.h       | 25 +++++++---------------
 include/linux/exportfs_block.h | 39 ++++++++++++++++++++++++++++++++++
 8 files changed, 74 insertions(+), 42 deletions(-)
 create mode 100644 include/linux/exportfs_block.h

diff --git a/MAINTAINERS b/MAINTAINERS
index c3fe46d7c4bc..b531b87a007e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9856,7 +9856,7 @@ S:	Supported
 F:	Documentation/filesystems/nfs/exporting.rst
 F:	fs/exportfs/
 F:	fs/fhandle.c
-F:	include/linux/exportfs.h
+F:	include/linux/exportfs*.h
 
 FILESYSTEMS [IDMAPPED MOUNTS]
 M:	Christian Brauner <brauner@kernel.org>
diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index 5ee5735b39bb..3cc3b47361e2 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -2,7 +2,7 @@
 /*
  * Copyright (c) 2014-2016 Christoph Hellwig.
  */
-#include <linux/exportfs.h>
+#include <linux/exportfs_block.h>
 #include <linux/iomap.h>
 #include <linux/slab.h>
 #include <linux/pr.h>
@@ -32,8 +32,8 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
 	u32 device_generation = 0;
 	int error;
 
-	error = sb->s_export_op->map_blocks(inode, offset, length, &iomap,
-			iomode != IOMODE_READ, &device_generation);
+	error = sb->s_export_op->block_ops->map_blocks(inode, offset, length,
+			&iomap, iomode != IOMODE_READ, &device_generation);
 	if (error) {
 		if (error == -ENXIO)
 			return nfserr_layoutunavailable;
@@ -199,8 +199,8 @@ nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
 		iattr.ia_size = lcp->lc_newsize;
 	}
 
-	error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,
-			nr_iomaps, &iattr);
+	error = inode->i_sb->s_export_op->block_ops->commit_blocks(inode,
+			iomaps, nr_iomaps, &iattr);
 	kfree(iomaps);
 	return nfserrno(error);
 }
@@ -223,8 +223,8 @@ nfsd4_block_get_device_info_simple(struct super_block *sb,
 
 	b->type = PNFS_BLOCK_VOLUME_SIMPLE;
 	b->simple.sig_len = PNFS_BLOCK_UUID_LEN;
-	return sb->s_export_op->get_uuid(sb, b->simple.sig, &b->simple.sig_len,
-			&b->simple.offset);
+	return sb->s_export_op->block_ops->get_uuid(sb, b->simple.sig,
+			&b->simple.sig_len, &b->simple.offset);
 }
 
 static __be32
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index ad7af8cfcf1f..6c1ff21b83a2 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -3,6 +3,7 @@
  * Copyright (c) 2014 Christoph Hellwig.
  */
 #include <linux/blkdev.h>
+#include <linux/exportfs_block.h>
 #include <linux/kmod.h>
 #include <linux/file.h>
 #include <linux/jhash.h>
@@ -127,6 +128,7 @@ void nfsd4_setup_layout_type(struct svc_export *exp)
 {
 #if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
 	struct super_block *sb = exp->ex_path.mnt->mnt_sb;
+	const struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
 #endif
 
 	if (!(exp->ex_flags & NFSEXP_PNFS))
@@ -136,14 +138,11 @@ void nfsd4_setup_layout_type(struct svc_export *exp)
 	exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
 #endif
 #ifdef CONFIG_NFSD_BLOCKLAYOUT
-	if (sb->s_export_op->get_uuid &&
-	    sb->s_export_op->map_blocks &&
-	    sb->s_export_op->commit_blocks)
+	if (bops && bops->get_uuid && bops->map_blocks && bops->commit_blocks)
 		exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
 #endif
 #ifdef CONFIG_NFSD_SCSILAYOUT
-	if (sb->s_export_op->map_blocks &&
-	    sb->s_export_op->commit_blocks &&
+	if (bops && bops->map_blocks && bops->commit_blocks &&
 	    sb->s_bdev &&
 	    sb->s_bdev->bd_disk->fops->pr_ops &&
 	    sb->s_bdev->bd_disk->fops->get_unique_id)
diff --git a/fs/xfs/xfs_export.c b/fs/xfs/xfs_export.c
index e3e3c3c89840..9b2ad3786b19 100644
--- a/fs/xfs/xfs_export.c
+++ b/fs/xfs/xfs_export.c
@@ -244,8 +244,6 @@ const struct export_operations xfs_export_operations = {
 	.get_parent		= xfs_fs_get_parent,
 	.commit_metadata	= xfs_fs_nfs_commit_metadata,
 #ifdef CONFIG_EXPORTFS_BLOCK_OPS
-	.get_uuid		= xfs_fs_get_uuid,
-	.map_blocks		= xfs_fs_map_blocks,
-	.commit_blocks		= xfs_fs_commit_blocks,
+	.block_ops		= &xfs_export_block_ops,
 #endif
 };
diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index 221e55887a2a..12e083f1b9ba 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -49,7 +49,7 @@ xfs_break_leased_layouts(
  * Get a unique ID including its location so that the client can identify
  * the exported device.
  */
-int
+static int
 xfs_fs_get_uuid(
 	struct super_block	*sb,
 	u8			*buf,
@@ -104,7 +104,7 @@ xfs_fs_map_update_inode(
 /*
  * Get a layout for the pNFS client.
  */
-int
+static int
 xfs_fs_map_blocks(
 	struct inode		*inode,
 	loff_t			offset,
@@ -252,7 +252,7 @@ xfs_pnfs_validate_isize(
  * to manually flush the cache here similar to what the fsync code path does
  * for datasyncs on files that have no dirty metadata.
  */
-int
+static int
 xfs_fs_commit_blocks(
 	struct inode		*inode,
 	struct iomap		*maps,
@@ -332,3 +332,9 @@ xfs_fs_commit_blocks(
 	xfs_iunlock(ip, XFS_IOLOCK_EXCL);
 	return error;
 }
+
+const struct exportfs_block_ops xfs_export_block_ops = {
+	.get_uuid		= xfs_fs_get_uuid,
+	.map_blocks		= xfs_fs_map_blocks,
+	.commit_blocks		= xfs_fs_commit_blocks,
+};
diff --git a/fs/xfs/xfs_pnfs.h b/fs/xfs/xfs_pnfs.h
index 940c6c2ad88c..bf43b2009e4c 100644
--- a/fs/xfs/xfs_pnfs.h
+++ b/fs/xfs/xfs_pnfs.h
@@ -2,13 +2,9 @@
 #ifndef _XFS_PNFS_H
 #define _XFS_PNFS_H 1
 
-#ifdef CONFIG_EXPORTFS_BLOCK_OPS
-int xfs_fs_get_uuid(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
-int xfs_fs_map_blocks(struct inode *inode, loff_t offset, u64 length,
-		struct iomap *iomap, bool write, u32 *device_generation);
-int xfs_fs_commit_blocks(struct inode *inode, struct iomap *maps, int nr_maps,
-		struct iattr *iattr);
+#include <linux/exportfs_block.h>
 
+#ifdef CONFIG_EXPORTFS_BLOCK_OPS
 int xfs_break_leased_layouts(struct inode *inode, uint *iolock,
 		bool *did_unlock);
 #else
@@ -18,4 +14,7 @@ xfs_break_leased_layouts(struct inode *inode, uint *iolock, bool *did_unlock)
 	return 0;
 }
 #endif /* CONFIG_EXPORTFS_BLOCK_OPS */
+
+extern const struct exportfs_block_ops xfs_export_block_ops;
+
 #endif /* _XFS_PNFS_H */
diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h
index 8bcdba28b406..c835bc64f4fa 100644
--- a/include/linux/exportfs.h
+++ b/include/linux/exportfs.h
@@ -6,9 +6,8 @@
 #include <linux/path.h>
 
 struct dentry;
-struct iattr;
+struct exportfs_block_ops;
 struct inode;
-struct iomap;
 struct super_block;
 struct vfsmount;
 
@@ -260,19 +259,13 @@ struct handle_to_path_ctx {
  * @commit_metadata:
  *    @commit_metadata should commit metadata changes to stable storage.
  *
- * @get_uuid:
- *    Get a filesystem unique signature exposed to clients.
- *
- * @map_blocks:
- *    Map and, if necessary, allocate blocks for a layout.
- *
- * @commit_blocks:
- *    Commit blocks in a layout once the client is done with them.
- *
  * @flags:
  *    Allows the filesystem to communicate to nfsd that it may want to do things
  *    differently when dealing with it.
  *
+ * @block_ops:
+ *    Operations for layout grants to block on the underlying device.
+ *
  * Locking rules:
  *    get_parent is called with child->d_inode->i_rwsem down
  *    get_name is not (which is possibly inconsistent)
@@ -290,12 +283,6 @@ struct export_operations {
 	struct dentry * (*get_parent)(struct dentry *child);
 	int (*commit_metadata)(struct inode *inode);
 
-	int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
-	int (*map_blocks)(struct inode *inode, loff_t offset,
-			  u64 len, struct iomap *iomap,
-			  bool write, u32 *device_generation);
-	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
-			     int nr_iomaps, struct iattr *iattr);
 	int (*permission)(struct handle_to_path_ctx *ctx, unsigned int oflags);
 	struct file * (*open)(const struct path *path, unsigned int oflags);
 #define	EXPORT_OP_NOWCC			(0x1) /* don't collect v3 wcc data */
@@ -308,6 +295,10 @@ struct export_operations {
 #define EXPORT_OP_FLUSH_ON_CLOSE	(0x20) /* fs flushes file data on close */
 #define EXPORT_OP_NOLOCKS		(0x40) /* no file locking support */
 	unsigned long	flags;
+
+#ifdef CONFIG_EXPORTFS_BLOCK_OPS
+	const struct exportfs_block_ops *block_ops;
+#endif
 };
 
 /**
diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
new file mode 100644
index 000000000000..1f52fea8e4dc
--- /dev/null
+++ b/include/linux/exportfs_block.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2014-2026 Christoph Hellwig.
+ *
+ * Support for exportfs-based layout grants for direct block device access.
+ */
+#ifndef LINUX_EXPORTFS_BLOCK_H
+#define LINUX_EXPORTFS_BLOCK_H 1
+
+#include <linux/types.h>
+
+struct iattr;
+struct inode;
+struct iomap;
+struct super_block;
+
+struct exportfs_block_ops {
+	/*
+	 * Get the in-band device unique signature exposed to clients.
+	 */
+	int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
+
+	/*
+	 * Map blocks for direct block access.
+	 * If @write is %true, also allocate the blocks for the range if needed.
+	 */
+	int (*map_blocks)(struct inode *inode, loff_t offset, u64 len,
+			struct iomap *iomap, bool write,
+			u32 *device_generation);
+
+	/*
+	 * Commit blocks previously handed out by ->map_blocks and written to by
+	 * the client.
+	 */
+	int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
+			int nr_iomaps, struct iattr *iattr);
+};
+
+#endif /* LINUX_EXPORTFS_BLOCK_H */
-- 
2.47.3


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

end of thread, other threads:[~2026-04-01 14:41 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-31 15:33 cleanup block-style layouts exports Christoph Hellwig
2026-03-31 15:33 ` [PATCH 1/4] nfsd/blocklayout: always ignore loca_time_modify Christoph Hellwig
2026-03-31 17:09   ` Jeff Layton
2026-04-01 14:25     ` Christoph Hellwig
2026-03-31 15:33 ` [PATCH 2/4] exportfs: split out the ops for layout-based block device access Christoph Hellwig
2026-03-31 17:24   ` Jeff Layton
2026-04-01 14:27     ` Christoph Hellwig
2026-03-31 18:07   ` Chuck Lever
2026-04-01  6:23     ` Christoph Hellwig
2026-03-31 15:33 ` [PATCH 3/4] exportfs: don't pass struct iattr to ->commit_blocks Christoph Hellwig
2026-03-31 17:26   ` Jeff Layton
2026-03-31 17:30     ` Chuck Lever
2026-03-31 17:33       ` Jeff Layton
2026-04-01  6:24         ` Christoph Hellwig
2026-03-31 15:33 ` [PATCH 4/4] exportfs,nfsd: rework checking for layout-based block device access support Christoph Hellwig
2026-03-31 17:52   ` Jeff Layton
2026-03-31 18:00   ` Chuck Lever
2026-03-31 17:33 ` cleanup block-style layouts exports Chuck Lever
2026-04-01  6:19   ` Christoph Hellwig
2026-04-01 13:39     ` Chuck Lever
  -- strict thread matches above, loose matches on Subject: below --
2026-04-01 14:40 cleanup block-style layouts exports v2 Christoph Hellwig
2026-04-01 14:40 ` [PATCH 2/4] exportfs: split out the ops for layout-based block device access Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox