All of lore.kernel.org
 help / color / mirror / Atom feed
From: Keith Busch via Linux-f2fs-devel <linux-f2fs-devel@lists.sourceforge.net>
To: <linux-block@vger.kernel.org>, <linux-ext4@vger.kernel.org>,
	<linux-f2fs-devel@lists.sourceforge.net>,
	<linux-fsdevel@vger.kernel.org>, <linux-xfs@vger.kernel.org>
Cc: axboe@kernel.dk, brauner@kernel.org, aalbersh@kernel.org,
	jack@suse.cz, Christoph Hellwig <hch@lst.de>,
	tytso@mit.edu, Keith Busch <kbusch@kernel.org>,
	jaegeuk@kernel.org, cem@kernel.org
Subject: [f2fs-dev] [PATCH] fs: report direct io constraints through file_getattr
Date: Tue, 7 Jul 2026 18:18:43 -0700	[thread overview]
Message-ID: <20260708011843.1036846-1-kbusch@meta.com> (raw)

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

             reply	other threads:[~2026-07-08  1:49 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-08  1:18 Keith Busch via Linux-f2fs-devel [this message]
2026-07-09  7:13 ` [PATCH] fs: report direct io constraints through file_getattr 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260708011843.1036846-1-kbusch@meta.com \
    --to=linux-f2fs-devel@lists.sourceforge.net \
    --cc=aalbersh@kernel.org \
    --cc=axboe@kernel.dk \
    --cc=brauner@kernel.org \
    --cc=cem@kernel.org \
    --cc=hch@lst.de \
    --cc=jack@suse.cz \
    --cc=jaegeuk@kernel.org \
    --cc=kbusch@kernel.org \
    --cc=kbusch@meta.com \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-xfs@vger.kernel.org \
    --cc=tytso@mit.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.