From: Prasad Singamsetty prasad.singamsetty@oracle.com
maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9VTE3 CVE: NA
Reference: https://lore.kernel.org/all/20240326133813.3224593-1-john.g.garry@oracle.com...
--------------------------------
Extend statx system call to return additional info for atomic write support support if the specified file is a block device.
Signed-off-by: Prasad Singamsetty prasad.singamsetty@oracle.com Signed-off-by: John Garry john.g.garry@oracle.com Signed-off-by: Long Li leo.lilong@huawei.com --- fs/block_dev.c | 32 ++++++++++++++++++++++++++++++++ fs/stat.c | 12 ++++++++++++ include/linux/blkdev.h | 7 +++++++ 3 files changed, 51 insertions(+)
diff --git a/fs/block_dev.c b/fs/block_dev.c index b9104fc0a395..749e36a49796 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -560,6 +560,38 @@ int sync_blockdev(struct block_device *bdev) } EXPORT_SYMBOL(sync_blockdev);
+/* + * Handle STATX_WRITE_ATOMIC for block devices. + */ +void bdev_statx(struct inode *backing_inode, struct kstat *stat, + u32 request_mask) +{ + struct block_device *bdev; + + if (!(request_mask & STATX_WRITE_ATOMIC)) + return; + + /* + * Note that backing_inode is the inode of a block device node file, + * not the block device's internal inode. Therefore it is *not* valid + * to use I_BDEV() here; the block device has to be looked up by i_rdev + * instead. + */ + bdev = blkdev_get_by_dev(backing_inode->i_rdev, FMODE_READ, NULL); + if (!bdev) + return; + + if (request_mask & STATX_WRITE_ATOMIC && bdev_can_atomic_write(bdev)) { + struct request_queue *bd_queue = bdev_get_queue(bdev);; + + generic_fill_statx_atomic_writes(stat, + queue_atomic_write_unit_min_bytes(bd_queue), + queue_atomic_write_unit_max_bytes(bd_queue)); + } + + blkdev_put(bdev, FMODE_READ); +} + /* * Write out and wait upon all dirty data associated with this * device. Filesystem data as well as the underlying block diff --git a/fs/stat.c b/fs/stat.c index f37e801fee98..1539882b483e 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -17,6 +17,7 @@ #include <linux/syscalls.h> #include <linux/pagemap.h> #include <linux/compat.h> +#include <linux/blkdev.h>
#include <linux/uaccess.h> #include <asm/unistd.h> @@ -207,6 +208,7 @@ static int vfs_statx(int dfd, const char __user *filename, int flags, { struct path path; unsigned lookup_flags = 0; + struct inode *backing_inode; int error;
if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH | @@ -231,6 +233,16 @@ static int vfs_statx(int dfd, const char __user *filename, int flags, if (path.mnt->mnt_root == path.dentry) stat->attributes |= STATX_ATTR_MOUNT_ROOT; stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT; + + /* + * If this is a block device inode, override the filesystem + * attributes with the block device specific parameters that need to be + * obtained from the bdev backing inode. + */ + backing_inode = d_backing_inode(path.dentry); + if (S_ISBLK(backing_inode->i_mode)) + bdev_statx(backing_inode, stat, request_mask); + path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index a53e70a6aeec..f0027ea2905d 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -2128,6 +2128,8 @@ void invalidate_bdev(struct block_device *bdev); int truncate_bdev_range(struct block_device *bdev, fmode_t mode, loff_t lstart, loff_t lend); int sync_blockdev(struct block_device *bdev); +void bdev_statx(struct inode *backing_inode, struct kstat *stat, + u32 request_mask); #else static inline void invalidate_bdev(struct block_device *bdev) { @@ -2141,6 +2143,11 @@ static inline int sync_blockdev(struct block_device *bdev) { return 0; } + +static inline void bdev_statx(struct inode *backing_inode, struct kstat *stat, + u32 request_mask) +{ +} #endif int fsync_bdev(struct block_device *bdev);