[f2fs-dev] [PATCH] fs: report direct io constraints through file_getattr

Keith Busch via Linux-f2fs-devel <[email protected]>
Newsgroups net.sourceforge.lists.linux-f2fs-devel
Message-ID <[email protected]>
From: Keith Busch <[email protected]>

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 <[email protected]>
Signed-off-by: Keith Busch <[email protected]>
---
 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
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.