[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