public inbox for linux-fsdevel@vger.kernel.org
 help / color / mirror / Atom feed
* support multiple block devices per file system for block-style layouts
@ 2026-03-23  7:07 Christoph Hellwig
  2026-03-23  7:07 ` [PATCH 1/7] exportfs: split out the ops for layout-based block device access Christoph Hellwig
                   ` (6 more replies)
  0 siblings, 7 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel

Hi all,

this series adds support for exporting multiple block devices using
block-style from nfsd, and uses that to implement support for the XFS
RT device.  To get there is also first cleans up the block-style layout
interface in exportfs.

Diffstat:
 MAINTAINERS                    |    2 
 fs/nfsd/blocklayout.c          |   69 ++++++++++++++++--------------
 fs/nfsd/export.c               |    3 -
 fs/nfsd/flexfilelayout.c       |    3 -
 fs/nfsd/nfs4layouts.c          |   32 ++++---------
 fs/nfsd/pnfs.h                 |    2 
 fs/nfsd/xdr4.h                 |    5 +-
 fs/xfs/xfs_export.c            |    4 -
 fs/xfs/xfs_pnfs.c              |   93 +++++++++++++++++++++++++++++++---------
 fs/xfs/xfs_pnfs.h              |   11 ++--
 include/linux/exportfs.h       |   25 +++-------
 include/linux/exportfs_block.h |   94 +++++++++++++++++++++++++++++++++++++++++
 12 files changed, 235 insertions(+), 108 deletions(-)

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

* [PATCH 1/7] exportfs: split out the ops for layout-based block device access
  2026-03-23  7:07 support multiple block devices per file system for block-style layouts Christoph Hellwig
@ 2026-03-23  7:07 ` Christoph Hellwig
  2026-03-23 13:39   ` Chuck Lever
  2026-03-23  7:07 ` [PATCH 2/7] exportfs: don't pass struct iattr to ->commit_blocks Christoph Hellwig
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, 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 7d10988cbc62..c93a3c336553 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9845,7 +9845,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 a7cfba29990e..7464e9e37af1 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;
@@ -194,8 +194,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);
 }
@@ -218,8 +218,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..616984fe3873 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->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->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] 15+ messages in thread

* [PATCH 2/7] exportfs: don't pass struct iattr to ->commit_blocks
  2026-03-23  7:07 support multiple block devices per file system for block-style layouts Christoph Hellwig
  2026-03-23  7:07 ` [PATCH 1/7] exportfs: split out the ops for layout-based block device access Christoph Hellwig
@ 2026-03-23  7:07 ` Christoph Hellwig
  2026-03-23 14:05   ` Chuck Lever
  2026-03-23  7:07 ` [PATCH 3/7] exportfs,nfsd: rework checking for layout-based block device access support Christoph Hellwig
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, 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          | 11 ++---------
 fs/xfs/xfs_pnfs.c              | 19 ++++++++++---------
 include/linux/exportfs_block.h |  3 +--
 3 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index 7464e9e37af1..0dd36f3d5a51 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -180,22 +180,15 @@ 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);
-	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
-	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
-
-	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] 15+ messages in thread

* [PATCH 3/7] exportfs,nfsd: rework checking for layout-based block device access support
  2026-03-23  7:07 support multiple block devices per file system for block-style layouts Christoph Hellwig
  2026-03-23  7:07 ` [PATCH 1/7] exportfs: split out the ops for layout-based block device access Christoph Hellwig
  2026-03-23  7:07 ` [PATCH 2/7] exportfs: don't pass struct iattr to ->commit_blocks Christoph Hellwig
@ 2026-03-23  7:07 ` Christoph Hellwig
  2026-03-23  7:07 ` [PATCH 4/7] nfsd: support multiple pNFS device IDs Christoph Hellwig
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, 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 616984fe3873..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->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->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] 15+ messages in thread

* [PATCH 4/7] nfsd: support multiple pNFS device IDs
  2026-03-23  7:07 support multiple block devices per file system for block-style layouts Christoph Hellwig
                   ` (2 preceding siblings ...)
  2026-03-23  7:07 ` [PATCH 3/7] exportfs,nfsd: rework checking for layout-based block device access support Christoph Hellwig
@ 2026-03-23  7:07 ` Christoph Hellwig
  2026-03-23  7:07 ` [PATCH 5/7] nfsd/blocklayout: support GETDEVICEINFO for multiple devices Christoph Hellwig
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel

Add support for a device index in the device ID so that we can support
multiple different devices and not just different generations when
the devids are refreshed.  This will be used for block layout exports
of multi-device file systems.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/nfsd/blocklayout.c    | 2 +-
 fs/nfsd/flexfilelayout.c | 3 +--
 fs/nfsd/nfs4layouts.c    | 3 ++-
 fs/nfsd/pnfs.h           | 2 +-
 fs/nfsd/xdr4.h           | 5 +++--
 5 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index 0dd36f3d5a51..8ca8fd8f70cb 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -75,7 +75,7 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
 		return nfserr_layoutunavailable;
 	}
 
-	error = nfsd4_set_deviceid(&bex->vol_id, fhp, device_generation);
+	error = nfsd4_set_deviceid(&bex->vol_id, fhp, 0, device_generation);
 	if (error)
 		return nfserrno(error);
 
diff --git a/fs/nfsd/flexfilelayout.c b/fs/nfsd/flexfilelayout.c
index 6d531285ab43..b57db4b4dda7 100644
--- a/fs/nfsd/flexfilelayout.c
+++ b/fs/nfsd/flexfilelayout.c
@@ -24,7 +24,6 @@ nfsd4_ff_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode,
 		const struct svc_fh *fhp, struct nfsd4_layoutget *args)
 {
 	struct nfsd4_layout_seg *seg = &args->lg_seg;
-	u32 device_generation = 0;
 	int error;
 	uid_t u;
 
@@ -57,7 +56,7 @@ nfsd4_ff_proc_layoutget(struct svc_rqst *rqstp, struct inode *inode,
 		fl->uid = inode->i_uid;
 	fl->gid = inode->i_gid;
 
-	error = nfsd4_set_deviceid(&fl->deviceid, fhp, device_generation);
+	error = nfsd4_set_deviceid(&fl->deviceid, fhp, 0, 0);
 	if (error)
 		goto out_error;
 
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 7b849b637b5e..ace6057f5d63 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -110,7 +110,7 @@ nfsd4_find_devid_map(int idx)
 
 int
 nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
-		u32 device_generation)
+		u32 dev_idx, u32 device_generation)
 {
 	if (!fhp->fh_export->ex_devid_map) {
 		nfsd4_alloc_devid_map(fhp);
@@ -120,6 +120,7 @@ nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
 
 	id->fsid_idx = fhp->fh_export->ex_devid_map->idx;
 	id->generation = device_generation;
+	id->dev_idx = dev_idx;
 	return 0;
 }
 
diff --git a/fs/nfsd/pnfs.h b/fs/nfsd/pnfs.h
index db9af780438b..4a6b0ad2ec9b 100644
--- a/fs/nfsd/pnfs.h
+++ b/fs/nfsd/pnfs.h
@@ -65,7 +65,7 @@ __be32 nfsd4_return_client_layouts(struct svc_rqst *rqstp,
 		struct nfsd4_compound_state *cstate,
 		struct nfsd4_layoutreturn *lrp);
 int nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
-		u32 device_generation);
+		u32 dev_idx, u32 device_generation);
 struct nfsd4_deviceid_map *nfsd4_find_devid_map(int idx);
 #endif /* CONFIG_NFSD_V4 */
 
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 417e9ad9fbb3..ddeba134b9af 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -602,6 +602,7 @@ struct nfsd4_reclaim_complete {
 struct nfsd4_deviceid {
 	u64			fsid_idx;
 	u32			generation;
+	u32			dev_idx;
 };
 
 static inline __be32 *
@@ -612,7 +613,7 @@ svcxdr_encode_deviceid4(__be32 *p, const struct nfsd4_deviceid *devid)
 	*q = (__force __be64)devid->fsid_idx;
 	p += 2;
 	*p++ = (__force __be32)devid->generation;
-	*p++ = xdr_zero;
+	*p++ = (__force __be32)devid->dev_idx;
 	return p;
 }
 
@@ -624,7 +625,7 @@ svcxdr_decode_deviceid4(__be32 *p, struct nfsd4_deviceid *devid)
 	devid->fsid_idx = (__force u64)(*q);
 	p += 2;
 	devid->generation = (__force u32)(*p++);
-	p++; /* NFSD does not use the remaining octets */
+	devid->dev_idx = (__force u32)(*p++);
 	return p;
 }
 
-- 
2.47.3


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

* [PATCH 5/7] nfsd/blocklayout: support GETDEVICEINFO for multiple devices
  2026-03-23  7:07 support multiple block devices per file system for block-style layouts Christoph Hellwig
                   ` (3 preceding siblings ...)
  2026-03-23  7:07 ` [PATCH 4/7] nfsd: support multiple pNFS device IDs Christoph Hellwig
@ 2026-03-23  7:07 ` Christoph Hellwig
  2026-03-23 13:53   ` Chuck Lever
  2026-03-23  7:07 ` [PATCH 6/7] exportfs: return a device index from ->map_blocks Christoph Hellwig
  2026-03-23  7:07 ` [PATCH 7/7] xfs: support layout-based block device access on the RT device Christoph Hellwig
  6 siblings, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel

Add a method to return the block_device for the given device index,
and use that in nfsd4_scsi_proc_getdeviceinfo to support multiple
devices for the SCSI layout.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/nfsd/blocklayout.c          | 41 +++++++++++++++++++++-------------
 include/linux/exportfs_block.h |  6 +++++
 2 files changed, 31 insertions(+), 16 deletions(-)

diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index 8ca8fd8f70cb..fb13f86f8eb5 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -303,15 +303,19 @@ nfsd4_block_get_unique_id(struct gendisk *disk, struct pnfs_block_volume *b)
 }
 
 static int
-nfsd4_block_get_device_info_scsi(struct super_block *sb,
-		struct nfs4_client *clp,
-		struct nfsd4_getdeviceinfo *gdp)
+nfsd4_block_get_device_info_scsi(struct block_device *bdev,
+		struct nfs4_client *clp, struct nfsd4_getdeviceinfo *gdp)
 {
 	struct pnfs_block_deviceaddr *dev;
 	struct pnfs_block_volume *b;
 	const struct pr_ops *ops;
 	int ret;
 
+	if (bdev_is_partition(bdev))
+		return -EINVAL;
+	if (!exportfs_bdev_supports_out_of_band_id(bdev))
+		return -EINVAL;
+
 	dev = kzalloc_flex(*dev, volumes, 1);
 	if (!dev)
 		return -ENOMEM;
@@ -323,30 +327,28 @@ nfsd4_block_get_device_info_scsi(struct super_block *sb,
 	b->type = PNFS_BLOCK_VOLUME_SCSI;
 	b->scsi.pr_key = nfsd4_scsi_pr_key(clp);
 
-	ret = nfsd4_block_get_unique_id(sb->s_bdev->bd_disk, b);
+	ret = nfsd4_block_get_unique_id(bdev->bd_disk, b);
 	if (ret < 0)
 		goto out_free_dev;
 
 	ret = -EINVAL;
-	ops = sb->s_bdev->bd_disk->fops->pr_ops;
+	ops = bdev->bd_disk->fops->pr_ops;
 	if (!ops) {
-		pr_err("pNFS: device %s does not support PRs.\n",
-			sb->s_id);
+		pr_err("pNFS: device %pg does not support persistent reservations.\n",
+			bdev);
 		goto out_free_dev;
 	}
 
-	ret = ops->pr_register(sb->s_bdev, 0, NFSD_MDS_PR_KEY, true);
+	ret = ops->pr_register(bdev, 0, NFSD_MDS_PR_KEY, true);
 	if (ret) {
-		pr_err("pNFS: failed to register key for device %s.\n",
-			sb->s_id);
+		pr_err("pNFS: failed to register key for device %pg.\n", bdev);
 		goto out_free_dev;
 	}
 
-	ret = ops->pr_reserve(sb->s_bdev, NFSD_MDS_PR_KEY,
+	ret = ops->pr_reserve(bdev, NFSD_MDS_PR_KEY,
 			PR_EXCLUSIVE_ACCESS_REG_ONLY, 0);
 	if (ret) {
-		pr_err("pNFS: failed to reserve device %s.\n",
-			sb->s_id);
+		pr_err("pNFS: failed to reserve device %pd.\n", bdev);
 		goto out_free_dev;
 	}
 
@@ -364,9 +366,16 @@ nfsd4_scsi_proc_getdeviceinfo(struct super_block *sb,
 		struct nfs4_client *clp,
 		struct nfsd4_getdeviceinfo *gdp)
 {
-	if (bdev_is_partition(sb->s_bdev))
-		return nfserr_inval;
-	return nfserrno(nfsd4_block_get_device_info_scsi(sb, clp, gdp));
+	struct block_device *bdev = sb->s_bdev;
+
+	if (sb->s_export_op->block_ops->devid_to_bdev) {
+		bdev = sb->s_export_op->block_ops->devid_to_bdev(sb,
+					gdp->gd_devid.dev_idx);
+		if (IS_ERR(bdev))
+			return nfserrno(PTR_ERR(bdev));
+	}
+
+	return nfserrno(nfsd4_block_get_device_info_scsi(bdev, clp, gdp));
 }
 static __be32
 nfsd4_scsi_proc_layoutcommit(struct inode *inode, struct svc_rqst *rqstp,
diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
index 8d5b0b0c5a82..a3f4054784e3 100644
--- a/include/linux/exportfs_block.h
+++ b/include/linux/exportfs_block.h
@@ -36,6 +36,12 @@ struct exportfs_block_ops {
 	 */
 	expfs_block_layouts_t (*layouts_supported)(struct super_block *sb);
 
+	/*
+	 * Map from an unsigned integer device index to a block device.
+	 */
+	struct block_device *(*devid_to_bdev)(struct super_block *sb,
+			u32 dev_idx);
+
 	/*
 	 * Get the in-band device unique signature exposed to clients.
 	 */
-- 
2.47.3


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

* [PATCH 6/7] exportfs: return a device index from ->map_blocks
  2026-03-23  7:07 support multiple block devices per file system for block-style layouts Christoph Hellwig
                   ` (4 preceding siblings ...)
  2026-03-23  7:07 ` [PATCH 5/7] nfsd/blocklayout: support GETDEVICEINFO for multiple devices Christoph Hellwig
@ 2026-03-23  7:07 ` Christoph Hellwig
  2026-03-23  7:07 ` [PATCH 7/7] xfs: support layout-based block device access on the RT device Christoph Hellwig
  6 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel

Allow file systems to return a device index from ->map_block that
indicates which device the layout is placed on.

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

diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
index fb13f86f8eb5..636ccefe1959 100644
--- a/fs/nfsd/blocklayout.c
+++ b/fs/nfsd/blocklayout.c
@@ -30,10 +30,12 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
 	struct super_block *sb = inode->i_sb;
 	struct iomap iomap;
 	u32 device_generation = 0;
+	u32 dev_idx = 0;
 	int error;
 
 	error = sb->s_export_op->block_ops->map_blocks(inode, offset, length,
-			&iomap, iomode != IOMODE_READ, &device_generation);
+			&iomap, iomode != IOMODE_READ, &dev_idx,
+			&device_generation);
 	if (error) {
 		if (error == -ENXIO)
 			return nfserr_layoutunavailable;
@@ -75,7 +77,8 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
 		return nfserr_layoutunavailable;
 	}
 
-	error = nfsd4_set_deviceid(&bex->vol_id, fhp, 0, device_generation);
+	error = nfsd4_set_deviceid(&bex->vol_id, fhp, dev_idx,
+			device_generation);
 	if (error)
 		return nfserrno(error);
 
diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index acefa0b99f53..7ef5a3f522f6 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -123,6 +123,7 @@ xfs_fs_map_blocks(
 	u64			length,
 	struct iomap		*iomap,
 	bool			write,
+	u32			*dev_idx,
 	u32			*device_generation)
 {
 	struct xfs_inode	*ip = XFS_I(inode);
@@ -139,6 +140,8 @@ xfs_fs_map_blocks(
 	if (xfs_is_shutdown(mp))
 		return -EIO;
 
+	*dev_idx = 0;
+
 	/*
 	 * We can't export inodes residing on the realtime device.  The realtime
 	 * device doesn't have a UUID to identify it, so the client has no way
diff --git a/include/linux/exportfs_block.h b/include/linux/exportfs_block.h
index a3f4054784e3..ec2f846c6b08 100644
--- a/include/linux/exportfs_block.h
+++ b/include/linux/exportfs_block.h
@@ -52,7 +52,7 @@ struct exportfs_block_ops {
 	 * 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,
+			struct iomap *iomap, bool write, u32 *dev_idx,
 			u32 *device_generation);
 
 	/*
-- 
2.47.3


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

* [PATCH 7/7] xfs: support layout-based block device access on the RT device
  2026-03-23  7:07 support multiple block devices per file system for block-style layouts Christoph Hellwig
                   ` (5 preceding siblings ...)
  2026-03-23  7:07 ` [PATCH 6/7] exportfs: return a device index from ->map_blocks Christoph Hellwig
@ 2026-03-23  7:07 ` Christoph Hellwig
  6 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-23  7:07 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel

Implement the devid_to_bdev method so that device index one can be
used for the RT device, and allow block-style layouts on the
conventional RT device.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_pnfs.c | 52 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 40 insertions(+), 12 deletions(-)

diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index 7ef5a3f522f6..4302df26bfc2 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -46,17 +46,50 @@ xfs_break_leased_layouts(
 	return error;
 }
 
+/*
+ * Check what layouts we support for direct block device access.
+ */
 static expfs_block_layouts_t
 xfs_fs_layouts_supported(
 	struct super_block	*sb)
 {
-	expfs_block_layouts_t	supported = EXPFS_BLOCK_IN_BAND_ID;
+	struct xfs_mount	*mp = XFS_M(sb);
+	expfs_block_layouts_t	supported = 0;
 
-	if (exportfs_bdev_supports_out_of_band_id(sb->s_bdev))
+	/*
+	 * We don't have a good way to identify a specific RT device.  And
+	 * there's really no point in trying hard just for the deprecated
+	 * block layout support.
+	 */
+	if (!xfs_has_realtime(mp))
+		supported |= EXPFS_BLOCK_IN_BAND_ID;
+
+	if (exportfs_bdev_supports_out_of_band_id(sb->s_bdev) ||
+	    (mp->m_rtdev_targp &&
+	     exportfs_bdev_supports_out_of_band_id(mp->m_rtdev_targp->bt_bdev)))
 		supported |= EXPFS_BLOCK_OUT_OF_BAND_ID;
 	return supported;
 }
 
+static struct block_device *
+xfs_fs_devid_to_bdev(
+	struct super_block	*sb,
+	u32			dev_idx)
+{
+	struct xfs_mount	*mp = XFS_M(sb);
+
+	switch (dev_idx) {
+	case 0:
+		return sb->s_bdev;
+	case 1:
+		if (mp->m_rtdev_targp && mp->m_rtdev_targp->bt_bdev)
+			return mp->m_rtdev_targp->bt_bdev;
+		fallthrough;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+}
+
 /*
  * Get a unique ID including its location so that the client can identify
  * the exported device.
@@ -141,20 +174,14 @@ xfs_fs_map_blocks(
 		return -EIO;
 
 	*dev_idx = 0;
-
-	/*
-	 * We can't export inodes residing on the realtime device.  The realtime
-	 * device doesn't have a UUID to identify it, so the client has no way
-	 * to find it.
-	 */
 	if (XFS_IS_REALTIME_INODE(ip))
-		return -ENXIO;
+		*dev_idx = 1;
 
 	/*
-	 * The pNFS block layout spec actually supports reflink like
-	 * functionality, but the Linux pNFS server doesn't implement it yet.
+	 * The pNFS block layout spec supports out of place writes, but the
+	 * Linux pNFS server doesn't implement it yet.
 	 */
-	if (xfs_is_reflink_inode(ip))
+	if (xfs_is_cow_inode(ip))
 		return -ENXIO;
 
 	/*
@@ -351,6 +378,7 @@ xfs_fs_commit_blocks(
 
 struct exportfs_block_ops xfs_export_block_ops = {
 	.layouts_supported	= xfs_fs_layouts_supported,
+	.devid_to_bdev		= xfs_fs_devid_to_bdev,
 	.get_uuid		= xfs_fs_get_uuid,
 	.map_blocks		= xfs_fs_map_blocks,
 	.commit_blocks		= xfs_fs_commit_blocks,
-- 
2.47.3


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

* Re: [PATCH 1/7] exportfs: split out the ops for layout-based block device access
  2026-03-23  7:07 ` [PATCH 1/7] exportfs: split out the ops for layout-based block device access Christoph Hellwig
@ 2026-03-23 13:39   ` Chuck Lever
  2026-03-26  5:33     ` Christoph Hellwig
  0 siblings, 1 reply; 15+ messages in thread
From: Chuck Lever @ 2026-03-23 13:39 UTC (permalink / raw)
  To: Christoph Hellwig, Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel



On Mon, Mar 23, 2026, at 3:07 AM, Christoph Hellwig wrote:

> diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
> index ad7af8cfcf1f..616984fe3873 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->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->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)

block_ops itself is NULL for any filesystem that does not provide
block layout support (everything other than XFS today).

When an admin exports such a filesystem with pNFS enabled
(NFSEXP_PNFS), svc_export_parse() calls nfsd4_setup_layout_type(),
and bops->get_uuid dereferences a NULL pointer.

Something like the following would restore the original behavior:

    if (bops && bops->get_uuid && ...)


-- 
Chuck Lever

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

* Re: [PATCH 5/7] nfsd/blocklayout: support GETDEVICEINFO for multiple devices
  2026-03-23  7:07 ` [PATCH 5/7] nfsd/blocklayout: support GETDEVICEINFO for multiple devices Christoph Hellwig
@ 2026-03-23 13:53   ` Chuck Lever
  2026-03-26  5:38     ` Christoph Hellwig
  0 siblings, 1 reply; 15+ messages in thread
From: Chuck Lever @ 2026-03-23 13:53 UTC (permalink / raw)
  To: Christoph Hellwig, Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel


On Mon, Mar 23, 2026, at 3:07 AM, Christoph Hellwig wrote:
> diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> index 8ca8fd8f70cb..fb13f86f8eb5 100644
> --- a/fs/nfsd/blocklayout.c
> +++ b/fs/nfsd/blocklayout.c
> @@ -303,15 +303,19 @@ nfsd4_block_get_unique_id(struct gendisk *disk, 
> struct pnfs_block_volume *b)
>  }
> 
>  static int
> -nfsd4_block_get_device_info_scsi(struct super_block *sb,
> -		struct nfs4_client *clp,
> -		struct nfsd4_getdeviceinfo *gdp)
> +nfsd4_block_get_device_info_scsi(struct block_device *bdev,
> +		struct nfs4_client *clp, struct nfsd4_getdeviceinfo *gdp)
>  {
>  	struct pnfs_block_deviceaddr *dev;
>  	struct pnfs_block_volume *b;
>  	const struct pr_ops *ops;
>  	int ret;
> 
> +	if (bdev_is_partition(bdev))
> +		return -EINVAL;
> +	if (!exportfs_bdev_supports_out_of_band_id(bdev))
> +		return -EINVAL;
> +
>  	dev = kzalloc_flex(*dev, volumes, 1);
>  	if (!dev)
>  		return -ENOMEM;
> @@ -323,30 +327,28 @@ nfsd4_block_get_device_info_scsi(struct super_block *sb,
>  	b->type = PNFS_BLOCK_VOLUME_SCSI;
>  	b->scsi.pr_key = nfsd4_scsi_pr_key(clp);
> 
> -	ret = nfsd4_block_get_unique_id(sb->s_bdev->bd_disk, b);
> +	ret = nfsd4_block_get_unique_id(bdev->bd_disk, b);
>  	if (ret < 0)
>  		goto out_free_dev;
> 
>  	ret = -EINVAL;
> -	ops = sb->s_bdev->bd_disk->fops->pr_ops;
> +	ops = bdev->bd_disk->fops->pr_ops;
>  	if (!ops) {
> -		pr_err("pNFS: device %s does not support PRs.\n",
> -			sb->s_id);
> +		pr_err("pNFS: device %pg does not support persistent reservations.\n",
> +			bdev);
>  		goto out_free_dev;
>  	}
> 
> -	ret = ops->pr_register(sb->s_bdev, 0, NFSD_MDS_PR_KEY, true);
> +	ret = ops->pr_register(bdev, 0, NFSD_MDS_PR_KEY, true);
>  	if (ret) {
> -		pr_err("pNFS: failed to register key for device %s.\n",
> -			sb->s_id);
> +		pr_err("pNFS: failed to register key for device %pg.\n", bdev);
>  		goto out_free_dev;
>  	}
> 
> -	ret = ops->pr_reserve(sb->s_bdev, NFSD_MDS_PR_KEY,
> +	ret = ops->pr_reserve(bdev, NFSD_MDS_PR_KEY,
>  			PR_EXCLUSIVE_ACCESS_REG_ONLY, 0);
>  	if (ret) {
> -		pr_err("pNFS: failed to reserve device %s.\n",
> -			sb->s_id);
> +		pr_err("pNFS: failed to reserve device %pd.\n", bdev);
>  		goto out_free_dev;
>  	}
> 
> @@ -364,9 +366,16 @@ nfsd4_scsi_proc_getdeviceinfo(struct super_block *sb,
>  		struct nfs4_client *clp,
>  		struct nfsd4_getdeviceinfo *gdp)
>  {
> -	if (bdev_is_partition(sb->s_bdev))
> -		return nfserr_inval;
> -	return nfserrno(nfsd4_block_get_device_info_scsi(sb, clp, gdp));
> +	struct block_device *bdev = sb->s_bdev;
> +
> +	if (sb->s_export_op->block_ops->devid_to_bdev) {
> +		bdev = sb->s_export_op->block_ops->devid_to_bdev(sb,
> +					gdp->gd_devid.dev_idx);
> +		if (IS_ERR(bdev))
> +			return nfserrno(PTR_ERR(bdev));
> +	}
> +
> +	return nfserrno(nfsd4_block_get_device_info_scsi(bdev, clp, gdp));
>  }
>  static __be32
>  nfsd4_scsi_proc_layoutcommit(struct inode *inode, struct svc_rqst *rqstp,

LLM review identified this issue:

402 static void
403 nfsd4_scsi_fence_client(struct nfs4_layout_stateid *ls, struct nfsd_file *file)
404 {
405         struct nfs4_client *clp = ls->ls_stid.sc_client;
406         struct block_device *bdev = file->nf_file->f_path.mnt->mnt_sb->s_bdev;
407         int status;
408 

This always gets the data device. For an RT inode whose layout was
granted with dev_idx=1, PR registration happened on the RT bdev (in
nfsd4_block_get_device_info_scsi after devid_to_bdev), but the
preempt here fires against the data bdev.
                                                                                                                   
The difficulty is that fence_client receives a nfs4_layout_stateid
and nfsd_file, neither of which carries the dev_idx that was encoded
in the device ID at layout grant time.

So the fix isn't just a one-liner — it requires threading the device
index (or the bdev) through to the fence path, likely by storing it
in the layout stateid when the layout is granted.

Pre-series invariant (broken by this series):                                           
                                                                                                                   
Before the series, nfsd4_setup_layout_type checked
sb->s_bdev->bd_disk->fops->pr_ops directly before enabling LAYOUT_SCSI.
That guaranteed fence_client could safely dereference sb->s_bdev's
pr_ops. After the series, the check delegates to
xfs_fs_layouts_supported, which uses:
                  
  if (exportfs_bdev_supports_out_of_band_id(sb->s_bdev) ||
      (mp->m_rtdev_targp &&
       exportfs_bdev_supports_out_of_band_id(mp->m_rtdev_targp->bt_bdev)))
      supported |= EXPFS_BLOCK_OUT_OF_BAND_ID;                                                                     

This means LAYOUT_SCSI can be enabled when only the RT device has
pr_ops and get_unique_id.

Concrete path to NULL deref:

  1. XFS with RT: data device is plain SATA (no pr_ops), RT device is NVMe (has pr_ops + get_unique_id)
  2. xfs_fs_layouts_supported → EXPFS_BLOCK_OUT_OF_BAND_ID set (via RT device)
  3. nfsd4_setup_layout_type → enables LAYOUT_SCSI
  4. Client does LAYOUTGET for RT inode → map_blocks sets dev_idx=1
  5. Client does GETDEVICEINFO with dev_idx=1 → devid_to_bdev returns RT bdev → PR register/reserve on RT bdev succeeds
  6. Recall fails → nfsd4_scsi_fence_client (line 406): bdev = ...->s_bdev (data device)
  7. Line 409: bdev->bd_disk->fops->pr_ops->pr_preempt(...) — pr_ops is NULL → kernel oops

Even when both devices have pr_ops, the fence still targets the
wrong device: the client's PR registration is on the RT bdev (done
during GETDEVICEINFO), but the preempt fires on the data bdev,
leaving the RT device unfenced.

The issue is reachable on any XFS+RT configuration where the RT
device supports SCSI persistent reservations but the data device
does not.


-- 
Chuck Lever

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

* Re: [PATCH 2/7] exportfs: don't pass struct iattr to ->commit_blocks
  2026-03-23  7:07 ` [PATCH 2/7] exportfs: don't pass struct iattr to ->commit_blocks Christoph Hellwig
@ 2026-03-23 14:05   ` Chuck Lever
  2026-03-26  5:36     ` Christoph Hellwig
  0 siblings, 1 reply; 15+ messages in thread
From: Chuck Lever @ 2026-03-23 14:05 UTC (permalink / raw)
  To: Christoph Hellwig, Chuck Lever, Jeff Layton, Amir Goldstein
  Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel


On Mon, Mar 23, 2026, at 3:07 AM, Christoph Hellwig wrote:
> diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c
> index 7464e9e37af1..0dd36f3d5a51 100644
> --- a/fs/nfsd/blocklayout.c
> +++ b/fs/nfsd/blocklayout.c
> @@ -180,22 +180,15 @@ 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);
> -	iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
> -	iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
> -
> -	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);
>  }

After this change is applied, nfsd4_block_commit_blocks continues
to compute lcp->lc_mtime (honoring UTIME_NOW etc.) but never
passes it to the underlying filesystem. The old code set

  iattr.ia_mtime = lcp->lc_mtime

and propagated it via setattr_copy; but now xfs_fs_commit_blocks
unconditionally uses inode_set_ctime_current(). The mtime
computation here is now dead code.

This change introduces a regression against RFC 8881 Section 18.42
(LAYOUTCOMMIT's loca_time_modify is ignored).


-- 
Chuck Lever

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

* Re: [PATCH 1/7] exportfs: split out the ops for layout-based block device access
  2026-03-23 13:39   ` Chuck Lever
@ 2026-03-26  5:33     ` Christoph Hellwig
  0 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-26  5:33 UTC (permalink / raw)
  To: Chuck Lever
  Cc: Christoph Hellwig, Chuck Lever, Jeff Layton, Amir Goldstein,
	NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel

On Mon, Mar 23, 2026 at 09:39:48AM -0400, Chuck Lever wrote:
> > -	    sb->s_export_op->commit_blocks &&
> > +	if (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)
> 
> block_ops itself is NULL for any filesystem that does not provide
> block layout support (everything other than XFS today).
> 
> When an admin exports such a filesystem with pNFS enabled
> (NFSEXP_PNFS), svc_export_parse() calls nfsd4_setup_layout_type(),
> and bops->get_uuid dereferences a NULL pointer.
> 
> Something like the following would restore the original behavior:
> 
>     if (bops && bops->get_uuid && ...)

Yes, I'll fix it up.


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

* Re: [PATCH 2/7] exportfs: don't pass struct iattr to ->commit_blocks
  2026-03-23 14:05   ` Chuck Lever
@ 2026-03-26  5:36     ` Christoph Hellwig
  0 siblings, 0 replies; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-26  5:36 UTC (permalink / raw)
  To: Chuck Lever
  Cc: Christoph Hellwig, Chuck Lever, Jeff Layton, Amir Goldstein,
	NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel

On Mon, Mar 23, 2026 at 10:05:14AM -0400, Chuck Lever wrote:
> After this change is applied, nfsd4_block_commit_blocks continues
> to compute lcp->lc_mtime (honoring UTIME_NOW etc.) but never
> passes it to the underlying filesystem. The old code set
> 
>   iattr.ia_mtime = lcp->lc_mtime
> 
> and propagated it via setattr_copy; but now xfs_fs_commit_blocks
> unconditionally uses inode_set_ctime_current(). The mtime
> computation here is now dead code.
> 
> This change introduces a regression against RFC 8881 Section 18.42
> (LAYOUTCOMMIT's loca_time_modify is ignored).

That's not new in this patch.  For the is_mgtime() case, which applies
to XFS, setattr_copy always sets mtime to the current time when
ATTR_MTIME is passed.  We'd need to move ATTR_MTIME_SET to set a
specific time.


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

* Re: [PATCH 5/7] nfsd/blocklayout: support GETDEVICEINFO for multiple devices
  2026-03-23 13:53   ` Chuck Lever
@ 2026-03-26  5:38     ` Christoph Hellwig
  2026-03-26 12:37       ` Chuck Lever
  0 siblings, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2026-03-26  5:38 UTC (permalink / raw)
  To: Chuck Lever
  Cc: Christoph Hellwig, Chuck Lever, Jeff Layton, Amir Goldstein,
	NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey,
	Carlos Maiolino, linux-nfs, linux-xfs, linux-fsdevel

On Mon, Mar 23, 2026 at 09:53:33AM -0400, Chuck Lever wrote:

[full quote reviewed, can you please trim your replies to the relevant
 parts?]

> This always gets the data device. For an RT inode whose layout was
> granted with dev_idx=1, PR registration happened on the RT bdev (in
> nfsd4_block_get_device_info_scsi after devid_to_bdev), but the
> preempt here fires against the data bdev.

That's correct.

> The difficulty is that fence_client receives a nfs4_layout_stateid
> and nfsd_file, neither of which carries the dev_idx that was encoded
> in the device ID at layout grant time.
> 
> So the fix isn't just a one-liner — it requires threading the device
> index (or the bdev) through to the fence path, likely by storing it
> in the layout stateid when the layout is granted.

This is bollocks.  A fence happens when the client fails to respond,
so we need to fence fence access to all device for it.  Which means
we just need to track which devices it got access to.  I'll look into
it.


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

* Re: [PATCH 5/7] nfsd/blocklayout: support GETDEVICEINFO for multiple devices
  2026-03-26  5:38     ` Christoph Hellwig
@ 2026-03-26 12:37       ` Chuck Lever
  0 siblings, 0 replies; 15+ messages in thread
From: Chuck Lever @ 2026-03-26 12:37 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Chuck Lever, Jeff Layton, Amir Goldstein, NeilBrown,
	Olga Kornievskaia, Dai Ngo, Tom Talpey, Carlos Maiolino,
	linux-nfs, linux-xfs, linux-fsdevel

On 3/26/26 1:38 AM, Christoph Hellwig wrote:
> On Mon, Mar 23, 2026 at 09:53:33AM -0400, Chuck Lever wrote:
> 
> [full quote reviewed, can you please trim your replies to the relevant
>  parts?]
All three of my replies to this series were trimmed. I find your replies
so aggressively trimmed that I lose context. It seems to be a matter of
taste.


-- 
Chuck Lever

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

end of thread, other threads:[~2026-03-26 12:37 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-23  7:07 support multiple block devices per file system for block-style layouts Christoph Hellwig
2026-03-23  7:07 ` [PATCH 1/7] exportfs: split out the ops for layout-based block device access Christoph Hellwig
2026-03-23 13:39   ` Chuck Lever
2026-03-26  5:33     ` Christoph Hellwig
2026-03-23  7:07 ` [PATCH 2/7] exportfs: don't pass struct iattr to ->commit_blocks Christoph Hellwig
2026-03-23 14:05   ` Chuck Lever
2026-03-26  5:36     ` Christoph Hellwig
2026-03-23  7:07 ` [PATCH 3/7] exportfs,nfsd: rework checking for layout-based block device access support Christoph Hellwig
2026-03-23  7:07 ` [PATCH 4/7] nfsd: support multiple pNFS device IDs Christoph Hellwig
2026-03-23  7:07 ` [PATCH 5/7] nfsd/blocklayout: support GETDEVICEINFO for multiple devices Christoph Hellwig
2026-03-23 13:53   ` Chuck Lever
2026-03-26  5:38     ` Christoph Hellwig
2026-03-26 12:37       ` Chuck Lever
2026-03-23  7:07 ` [PATCH 6/7] exportfs: return a device index from ->map_blocks Christoph Hellwig
2026-03-23  7:07 ` [PATCH 7/7] xfs: support layout-based block device access on the RT device Christoph Hellwig

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