All of lore.kernel.org
 help / color / mirror / Atom feed
* [f2fs-dev] [PATCH] fs: report direct io constraints through file_getattr
@ 2026-07-08  1:18 Keith Busch via Linux-f2fs-devel
  2026-07-09  7:13   ` [f2fs-dev] " Christoph Hellwig
  2026-07-10 21:25   ` Eric Biggers via Linux-f2fs-devel
  0 siblings, 2 replies; 21+ messages in thread
From: Keith Busch via Linux-f2fs-devel @ 2026-07-08  1:18 UTC (permalink / raw)
  To: linux-block, linux-ext4, linux-f2fs-devel, linux-fsdevel,
	linux-xfs
  Cc: axboe, brauner, aalbersh, jack, Christoph Hellwig, tytso,
	Keith Busch, jaegeuk, cem

From: Keith Busch <kbusch@kernel.org>

Memory alignment constraints for direct io can vary depending on the
backing storage hardware. Provide support through file_getattr to report
the attributes necessary for applications to know how to construct valid
read and write requests.

Suggested-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Keith Busch <kbusch@kernel.org>
---
 block/bdev.c             | 27 +++++++++++++++++++++++++++
 fs/ext4/ioctl.c          | 22 ++++++++++++++++++++++
 fs/f2fs/file.c           | 16 ++++++++++++++++
 fs/file_attr.c           | 16 ++++++++++++++--
 fs/xfs/xfs_ioctl.c       | 16 ++++++++++++++++
 include/linux/blkdev.h   | 12 ++++++++++++
 include/linux/fileattr.h | 10 ++++++++--
 include/uapi/linux/fs.h  | 10 +++++++++-
 8 files changed, 124 insertions(+), 5 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index 85ce57bd2ae4f..5b3e27339854e 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -28,6 +28,7 @@
 #include <linux/part_stat.h>
 #include <linux/uaccess.h>
 #include <linux/stat.h>
+#include <linux/fileattr.h>
 #include "../fs/internal.h"
 #include "blk.h"
 
@@ -1353,6 +1354,32 @@ void bdev_statx(const struct path *path, struct kstat *stat, u32 request_mask)
 	blkdev_put_no_open(bdev);
 }
 
+/*
+ * Handle DIO alignment for block devices via fileattr.
+ */
+void bdev_fileattr(const struct inode *inode, struct file_kattr *fa)
+{
+	struct block_device *bdev;
+
+	memset(fa, 0, sizeof(*fa));
+	fa->fsx_valid = true;
+	fa->flags_valid = true;
+
+	bdev = blkdev_get_no_open(inode->i_rdev, false);
+	if (!bdev)
+		return;
+
+	fa->fsx_dio_mem_align = bdev_dma_alignment(bdev) + 1;
+	fa->fsx_dio_offset_align = bdev_logical_block_size(bdev);
+	fa->fsx_dio_read_offset_align = bdev_logical_block_size(bdev);
+	fa->fsx_dio_virt_boundary_align = bdev_virt_boundary_alignment(bdev);
+	fa->fsx_max_segments = bdev_max_segments(bdev);
+	fa->fsx_xflags |= FS_XFLAG_DIO;
+
+	blkdev_put_no_open(bdev);
+}
+EXPORT_SYMBOL_GPL(bdev_fileattr);
+
 bool disk_live(struct gendisk *disk)
 {
 	return !inode_unhashed(BD_INODE(disk->part0));
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index c8387e6a2c6e9..70b25f9e0ad5a 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -21,6 +21,7 @@
 #include <linux/iversion.h>
 #include <linux/fileattr.h>
 #include <linux/uuid.h>
+#include <linux/blkdev.h>
 #include "ext4_jbd2.h"
 #include "ext4.h"
 #include <linux/fsmap.h>
@@ -1005,6 +1006,27 @@ int ext4_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
 	if (ext4_has_feature_project(inode->i_sb))
 		fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
 
+	if (S_ISREG(inode->i_mode)) {
+		u32 dio_align = ext4_dio_alignment(inode);
+
+		if (dio_align != 0) {
+			struct block_device *bdev = inode->i_sb->s_bdev;
+
+			if (dio_align == 1) {
+				fa->fsx_dio_mem_align = bdev_dma_alignment(bdev) + 1;
+				fa->fsx_dio_offset_align = bdev_logical_block_size(bdev);
+				fa->fsx_dio_read_offset_align = bdev_logical_block_size(bdev);
+			} else {
+				fa->fsx_dio_mem_align = dio_align;
+				fa->fsx_dio_offset_align = dio_align;
+				fa->fsx_dio_read_offset_align = dio_align;
+			}
+			fa->fsx_dio_virt_boundary_align = bdev_virt_boundary_alignment(bdev);
+			fa->fsx_max_segments = bdev_max_segments(bdev);
+			fa->fsx_xflags |= FS_XFLAG_DIO;
+		}
+	}
+
 	return 0;
 }
 
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 4b52c56d71f07..72036ef1d7a8a 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -3472,6 +3472,22 @@ int f2fs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
 	if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)))
 		fa->fsx_projid = from_kprojid(&init_user_ns, fi->i_projid);
 
+	if (S_ISREG(inode->i_mode)) {
+		unsigned int bsize = i_blocksize(inode);
+		struct block_device *bdev = inode->i_sb->s_bdev;
+
+		if (!f2fs_force_buffered_io(inode, WRITE)) {
+			fa->fsx_dio_mem_align = bsize;
+			fa->fsx_dio_offset_align = bsize;
+			fa->fsx_dio_read_offset_align = bsize;
+			if (bdev) {
+				fa->fsx_dio_virt_boundary_align = bdev_virt_boundary_alignment(bdev);
+				fa->fsx_max_segments = bdev_max_segments(bdev);
+			}
+			fa->fsx_xflags |= FS_XFLAG_DIO;
+		}
+	}
+
 	return 0;
 }
 
diff --git a/fs/file_attr.c b/fs/file_attr.c
index bfb00d256dd56..09804ef9901b8 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -7,6 +7,7 @@
 #include <linux/export.h>
 #include <linux/syscalls.h>
 #include <linux/namei.h>
+#include <linux/blkdev.h>
 
 #include "internal.h"
 
@@ -88,8 +89,12 @@ int vfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
 	struct inode *inode = d_inode(dentry);
 	int error;
 
-	if (!inode->i_op->fileattr_get)
-		return -ENOIOCTLCMD;
+	if (!inode->i_op->fileattr_get) {
+		if (!S_ISBLK(inode->i_mode))
+			return -ENOIOCTLCMD;
+		bdev_fileattr(inode, fa);
+		return 0;
+	}
 
 	error = security_inode_file_getattr(dentry, fa);
 	if (error)
@@ -110,6 +115,11 @@ static void fileattr_to_file_attr(const struct file_kattr *fa,
 	fattr->fa_nextents = fa->fsx_nextents;
 	fattr->fa_projid = fa->fsx_projid;
 	fattr->fa_cowextsize = fa->fsx_cowextsize;
+	fattr->fa_dio_mem_align = fa->fsx_dio_mem_align;
+	fattr->fa_dio_offset_align = fa->fsx_dio_offset_align;
+	fattr->fa_dio_read_offset_align = fa->fsx_dio_read_offset_align;
+	fattr->fa_dio_virt_boundary_align = fa->fsx_dio_virt_boundary_align;
+	fattr->fa_max_segments = fa->fsx_max_segments;
 }
 
 /**
@@ -145,6 +155,8 @@ static int file_attr_to_fileattr(const struct file_attr *fattr,
 
 	if (fattr->fa_xflags & ~mask)
 		return -EINVAL;
+	if (fattr->fa_pad)
+		return -EINVAL;
 
 	fileattr_fill_xflags(fa, fattr->fa_xflags & ~FS_XFLAG_RDONLY_MASK);
 	fa->fsx_extsize = fattr->fa_extsize;
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 1b53701bebea1..15e14d1525281 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -49,6 +49,7 @@
 
 #include <linux/mount.h>
 #include <linux/fileattr.h>
+#include <linux/blkdev.h>
 
 /* Return 0 on success or positive error */
 int
@@ -517,6 +518,21 @@ xfs_fill_fsxattr(
 		fa->fsx_nextents = xfs_iext_count(ifp);
 	else
 		fa->fsx_nextents = xfs_ifork_nextents(ifp);
+
+	if (whichfork == XFS_DATA_FORK && S_ISREG(VFS_I(ip)->i_mode)) {
+		struct xfs_buftarg *target = xfs_inode_buftarg(ip);
+		struct block_device *bdev = target->bt_bdev;
+
+		fa->fsx_dio_mem_align = bdev_dma_alignment(bdev) + 1;
+		fa->fsx_dio_read_offset_align = bdev_logical_block_size(bdev);
+		if (xfs_is_cow_inode(ip))
+			fa->fsx_dio_offset_align = xfs_inode_alloc_unitsize(ip);
+		else
+			fa->fsx_dio_offset_align = fa->fsx_dio_read_offset_align;
+		fa->fsx_dio_virt_boundary_align = bdev_virt_boundary_alignment(bdev);
+		fa->fsx_max_segments = bdev_max_segments(bdev);
+		fa->fsx_xflags |= FS_XFLAG_DIO;
+	}
 }
 
 STATIC int
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 9213a5716f95a..5e83572d15286 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1607,6 +1607,16 @@ static inline unsigned int bdev_dma_alignment(struct block_device *bdev)
 	return queue_dma_alignment(bdev_get_queue(bdev));
 }
 
+static inline unsigned long bdev_virt_boundary_mask(struct block_device *bdev)
+{
+	return bdev_get_queue(bdev)->limits.virt_boundary_mask;
+}
+
+static inline unsigned int bdev_virt_boundary_alignment(struct block_device *bdev)
+{
+	return bdev_virt_boundary_mask(bdev) + 1;
+}
+
 static inline unsigned int
 blk_lim_dma_alignment_and_pad(struct queue_limits *lim)
 {
@@ -1805,6 +1815,8 @@ int sync_blockdev_range(struct block_device *bdev, loff_t lstart, loff_t lend);
 int sync_blockdev_nowait(struct block_device *bdev);
 void sync_bdevs(bool wait);
 void bdev_statx(const struct path *path, struct kstat *stat, u32 request_mask);
+struct file_kattr;
+void bdev_fileattr(const struct inode *inode, struct file_kattr *fa);
 void printk_all_partitions(void);
 int __init early_lookup_bdev(const char *pathname, dev_t *dev);
 #else
diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
index 58044b5980162..9eca31b8289fe 100644
--- a/include/linux/fileattr.h
+++ b/include/linux/fileattr.h
@@ -17,11 +17,11 @@
 /* Read-only inode flags */
 #define FS_XFLAG_RDONLY_MASK \
 	(FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR | FS_XFLAG_VERITY | \
-	 FS_XFLAG_CASEFOLD | FS_XFLAG_CASENONPRESERVING)
+	 FS_XFLAG_CASEFOLD | FS_XFLAG_CASENONPRESERVING | FS_XFLAG_DIO)
 
 /* Flags to indicate valid value of fsx_ fields */
 #define FS_XFLAG_VALUES_MASK \
-	(FS_XFLAG_EXTSIZE | FS_XFLAG_COWEXTSIZE)
+	(FS_XFLAG_EXTSIZE | FS_XFLAG_COWEXTSIZE | FS_XFLAG_DIO)
 
 /* Flags for directories */
 #define FS_XFLAG_DIRONLY_MASK \
@@ -49,6 +49,12 @@ struct file_kattr {
 	u32	fsx_nextents;	/* nextents field value (get)	*/
 	u32	fsx_projid;	/* project identifier (get/set) */
 	u32	fsx_cowextsize;	/* CoW extsize field value (get/set)*/
+	/* struct file_attr dio alignment: */
+	u32	fsx_dio_mem_align;
+	u32	fsx_dio_offset_align;
+	u32	fsx_dio_read_offset_align;
+	u32	fsx_dio_virt_boundary_align;
+	u32	fsx_max_segments;
 	/* selectors: */
 	bool	flags_valid:1;
 	bool	fsx_valid:1;
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index bd87262f2e349..2716fc6fefd6b 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -229,10 +229,17 @@ struct file_attr {
 	__u32 fa_nextents;	/* nextents field value (get)   */
 	__u32 fa_projid;	/* project identifier (get/set) */
 	__u32 fa_cowextsize;	/* CoW extsize field value (get/set) */
+	__u32 fa_dio_mem_align;	/* memory buffer alignment for direct I/O (get) */
+	__u32 fa_dio_offset_align;	/* file offset alignment for direct I/O (get) */
+	__u32 fa_dio_read_offset_align;	/* file offset alignment for direct I/O reads (get) */
+	__u32 fa_dio_virt_boundary_align;	/* virtual boundary alignment for direct I/O (get) */
+	__u32 fa_max_segments;	/* max number of segments for direct I/O (get) */
+	__u32 fa_pad;		/* padding for 8-byte alignment */
 };
 
 #define FILE_ATTR_SIZE_VER0 24
-#define FILE_ATTR_SIZE_LATEST FILE_ATTR_SIZE_VER0
+#define FILE_ATTR_SIZE_VER1 48
+#define FILE_ATTR_SIZE_LATEST FILE_ATTR_SIZE_VER1
 
 /*
  * Flags for the fsx_xflags field
@@ -261,6 +268,7 @@ struct file_attr {
  */
 #define FS_XFLAG_CASEFOLD	0x00040000	/* case-insensitive lookups */
 #define FS_XFLAG_CASENONPRESERVING 0x00080000	/* case not preserved */
+#define FS_XFLAG_DIO		0x00100000	/* DIO alignment info valid */
 #define FS_XFLAG_HASATTR	0x80000000	/* no DIFLAG for this	*/
 
 /* the read-only stuff doesn't really belong here, but any other place is
-- 
2.52.0



_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

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

end of thread, other threads:[~2026-07-10 23:11 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-08  1:18 [f2fs-dev] [PATCH] fs: report direct io constraints through file_getattr Keith Busch via Linux-f2fs-devel
2026-07-09  7:13 ` Christoph Hellwig
2026-07-09  7:13   ` [f2fs-dev] " Christoph Hellwig
2026-07-09  8:51   ` Andrey Albershteyn
2026-07-09  8:51     ` [f2fs-dev] " Andrey Albershteyn via Linux-f2fs-devel
2026-07-10 21:28     ` Eric Biggers
2026-07-10 21:28       ` Eric Biggers via Linux-f2fs-devel
2026-07-09  9:14   ` Jan Kara
2026-07-09  9:14     ` [f2fs-dev] " Jan Kara
2026-07-10 21:20     ` Eric Biggers via Linux-f2fs-devel
2026-07-10 21:20       ` Eric Biggers
2026-07-10 23:11       ` Keith Busch
2026-07-10 23:11         ` [f2fs-dev] " Keith Busch via Linux-f2fs-devel
2026-07-09 13:46   ` Keith Busch
2026-07-09 13:46     ` [f2fs-dev] " Keith Busch via Linux-f2fs-devel
2026-07-10  4:35     ` Christoph Hellwig
2026-07-10  4:35       ` [f2fs-dev] " Christoph Hellwig
2026-07-10 15:22       ` Keith Busch
2026-07-10 15:22         ` [f2fs-dev] " Keith Busch via Linux-f2fs-devel
2026-07-10 21:25 ` Eric Biggers
2026-07-10 21:25   ` Eric Biggers via Linux-f2fs-devel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.