[PATCH 15/21] fs: xfs: Support atomic write for statx

John Garry
2023-09-29T10:29:46Z

Support providing info on atomic write unit min and max for an inode.

For simplicity, currently we limit the min at the FS block size, but a
lower limit could be supported in future.

The atomic write unit min and max is limited by the guaranteed extent
alignment for the inode.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
fs/xfs/xfs_iops.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_iops.h | 4 ++++
2 files changed, 55 insertions(+)

diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 1c1e6171209d..5bff80748223 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -546,6 +546,46 @@ xfs_stat_blksize(
return PAGE_SIZE;
}

+void xfs_ip_atomic_write_attr(struct xfs_inode *ip,
+ xfs_filblks_t *unit_min_fsb,
+ xfs_filblks_t *unit_max_fsb)
+{
+ xfs_extlen_t extsz_hint = xfs_get_extsz_hint(ip);
+ struct xfs_buftarg *target = xfs_inode_buftarg(ip);
+ struct block_device *bdev = target->bt_bdev;
+ struct xfs_mount *mp = ip->i_mount;
+ xfs_filblks_t atomic_write_unit_min,
+ atomic_write_unit_max,
+ align;
+
+ atomic_write_unit_min = XFS_B_TO_FSB(mp,
+ queue_atomic_write_unit_min_bytes(bdev->bd_queue));
+ atomic_write_unit_max = XFS_B_TO_FSB(mp,
+ queue_atomic_write_unit_max_bytes(bdev->bd_queue));
+
+ /* for RT, unset extsize gives hint of 1 */
+ /* for !RT, unset extsize gives hint of 0 */
+ if (extsz_hint && (XFS_IS_REALTIME_INODE(ip) ||
+ (ip->i_diflags2 & XFS_DIFLAG2_FORCEALIGN)))
+ align = extsz_hint;
+ else
+ align = 1;
+
+ if (atomic_write_unit_max == 0) {
+ *unit_min_fsb = 0;
+ *unit_max_fsb = 0;
+ } else if (atomic_write_unit_min == 0) {
+ *unit_min_fsb = 1;
+ *unit_max_fsb = min_t(xfs_filblks_t, atomic_write_unit_max,
+ align);
+ } else {
+ *unit_min_fsb = min_t(xfs_filblks_t, atomic_write_unit_min,
+ align);
+ *unit_max_fsb = min_t(xfs_filblks_t, atomic_write_unit_max,
+ align);
+ }
+}
+
STATIC int
xfs_vn_getattr(
struct mnt_idmap *idmap,
@@ -614,6 +654,17 @@ xfs_vn_getattr(
stat->dio_mem_align = bdev_dma_alignment(bdev) + 1;
stat->dio_offset_align = bdev_logical_block_size(bdev);
}
+ if (request_mask & STATX_WRITE_ATOMIC) {
+ xfs_filblks_t unit_min_fsb, unit_max_fsb;
+
+ xfs_ip_atomic_write_attr(ip, &unit_min_fsb,
+ &unit_max_fsb);
+ stat->atomic_write_unit_min = XFS_FSB_TO_B(mp, unit_min_fsb);
+ stat->atomic_write_unit_max = XFS_FSB_TO_B(mp, unit_max_fsb);
+ stat->attributes |= STATX_ATTR_WRITE_ATOMIC;
+ stat->attributes_mask |= STATX_ATTR_WRITE_ATOMIC;
+ stat->result_mask |= STATX_WRITE_ATOMIC;
+ }
fallthrough;
default:
stat->blksize = xfs_stat_blksize(ip);
diff --git a/fs/xfs/xfs_iops.h b/fs/xfs/xfs_iops.h
index 7f84a0843b24..b1e683b04301 100644
--- a/fs/xfs/xfs_iops.h
+++ b/fs/xfs/xfs_iops.h
@@ -19,4 +19,8 @@ int xfs_vn_setattr_size(struct mnt_idmap *idmap,
int xfs_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr);

+void xfs_ip_atomic_write_attr(struct xfs_inode *ip,
+ xfs_filblks_t *unit_min_fsb,
+ xfs_filblks_t *unit_max_fsb);
+
#endif /* __XFS_IOPS_H__ */
--
2.31.1