fs: add atomic write unit max opt to statx
authorJohn Garry <john.g.garry@oracle.com>
Wed, 7 May 2025 21:18:21 +0000 (14:18 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 7 May 2025 21:25:30 +0000 (14:25 -0700)
XFS will be able to support large atomic writes (atomic write > 1x block)
in future. This will be achieved by using different operating methods,
depending on the size of the write.

Specifically a new method of operation based in FS atomic extent remapping
will be supported in addition to the current HW offload-based method.

The FS method will generally be appreciably slower performing than the
HW-offload method. However the FS method will be typically able to
contribute to achieving a larger atomic write unit max limit.

XFS will support a hybrid mode, where HW offload method will be used when
possible, i.e. HW offload is used when the length of the write is
supported, and for other times FS-based atomic writes will be used.

As such, there is an atomic write length at which the user may experience
appreciably slower performance.

Advertise this limit in a new statx field, stx_atomic_write_unit_max_opt.

When zero, it means that there is no such performance boundary.

Masks STATX{_ATTR}_WRITE_ATOMIC can be used to get this new field. This is
ok for older kernels which don't support this new field, as they would
report 0 in this field (from zeroing in cp_statx()) already. Furthermore
those older kernels don't support large atomic writes - apart from block
fops, but there would be consistent performance there for atomic writes
in range [unit min, unit max].

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Acked-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: John Garry <john.g.garry@oracle.com>
block/bdev.c
fs/ext4/inode.c
fs/stat.c
fs/xfs/xfs_iops.c
include/linux/fs.h
include/linux/stat.h
include/uapi/linux/stat.h

index 520515e4e64ef0a77f0758f537932e29c2887802..9f321fb94bac4758bd0bbc14bac9b39fc1b1d357 100644 (file)
@@ -1336,7 +1336,8 @@ void bdev_statx(struct path *path, struct kstat *stat,
 
                generic_fill_statx_atomic_writes(stat,
                        queue_atomic_write_unit_min_bytes(bd_queue),
-                       queue_atomic_write_unit_max_bytes(bd_queue));
+                       queue_atomic_write_unit_max_bytes(bd_queue),
+                       0);
        }
 
        stat->blksize = bdev_io_min(bdev);
index 94c7d2d828a64e42ded09c82497ed7617071aa19..cdf01e60fa6dace433b100ac32dd731681fceb02 100644 (file)
@@ -5692,7 +5692,7 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path,
                        awu_max = sbi->s_awu_max;
                }
 
-               generic_fill_statx_atomic_writes(stat, awu_min, awu_max);
+               generic_fill_statx_atomic_writes(stat, awu_min, awu_max, 0);
        }
 
        flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
index f13308bfdc983a1670f83ec23f235c17fb19adfc..c41855f62d226ec7e413bf0e33dfcc8c6a37a8c4 100644 (file)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -136,13 +136,15 @@ EXPORT_SYMBOL(generic_fill_statx_attr);
  * @stat:      Where to fill in the attribute flags
  * @unit_min:  Minimum supported atomic write length in bytes
  * @unit_max:  Maximum supported atomic write length in bytes
+ * @unit_max_opt: Optimised maximum supported atomic write length in bytes
  *
  * Fill in the STATX{_ATTR}_WRITE_ATOMIC flags in the kstat structure from
  * atomic write unit_min and unit_max values.
  */
 void generic_fill_statx_atomic_writes(struct kstat *stat,
                                      unsigned int unit_min,
-                                     unsigned int unit_max)
+                                     unsigned int unit_max,
+                                     unsigned int unit_max_opt)
 {
        /* Confirm that the request type is known */
        stat->result_mask |= STATX_WRITE_ATOMIC;
@@ -153,6 +155,7 @@ void generic_fill_statx_atomic_writes(struct kstat *stat,
        if (unit_min) {
                stat->atomic_write_unit_min = unit_min;
                stat->atomic_write_unit_max = unit_max;
+               stat->atomic_write_unit_max_opt = unit_max_opt;
                /* Initially only allow 1x segment */
                stat->atomic_write_segments_max = 1;
 
@@ -732,6 +735,7 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
        tmp.stx_atomic_write_unit_min = stat->atomic_write_unit_min;
        tmp.stx_atomic_write_unit_max = stat->atomic_write_unit_max;
        tmp.stx_atomic_write_segments_max = stat->atomic_write_segments_max;
+       tmp.stx_atomic_write_unit_max_opt = stat->atomic_write_unit_max_opt;
 
        return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 }
index 756bd3ca8e003b855506bfd2a98a3a1baf541863..f0e5d83195df7140aeb7d441d37b17a25a6bda61 100644 (file)
@@ -610,7 +610,7 @@ xfs_report_atomic_write(
 
        if (xfs_inode_can_atomicwrite(ip))
                unit_min = unit_max = ip->i_mount->m_sb.sb_blocksize;
-       generic_fill_statx_atomic_writes(stat, unit_min, unit_max);
+       generic_fill_statx_atomic_writes(stat, unit_min, unit_max, 0);
 }
 
 STATIC int
index 016b0fe1536e3630eccb10df3f6f93393c97966b..7b19d8f99aff24ec2e892091b896f7e7668ba5ae 100644 (file)
@@ -3475,7 +3475,8 @@ void generic_fillattr(struct mnt_idmap *, u32, struct inode *, struct kstat *);
 void generic_fill_statx_attr(struct inode *inode, struct kstat *stat);
 void generic_fill_statx_atomic_writes(struct kstat *stat,
                                      unsigned int unit_min,
-                                     unsigned int unit_max);
+                                     unsigned int unit_max,
+                                     unsigned int unit_max_opt);
 extern int vfs_getattr_nosec(const struct path *, struct kstat *, u32, unsigned int);
 extern int vfs_getattr(const struct path *, struct kstat *, u32, unsigned int);
 void __inode_add_bytes(struct inode *inode, loff_t bytes);
index be7496a6a0dd9c5a9a8047ebf07b5000d5b017c5..e3d00e7bb26d90949c67b16646a3b13bac16bc16 100644 (file)
@@ -57,6 +57,7 @@ struct kstat {
        u32             dio_read_offset_align;
        u32             atomic_write_unit_min;
        u32             atomic_write_unit_max;
+       u32             atomic_write_unit_max_opt;
        u32             atomic_write_segments_max;
 };
 
index f78ee3670dd5d7c897c42e9a97c1ba70856b18fa..1686861aae20a6e4f3aec35a301016deaecae870 100644 (file)
@@ -182,8 +182,12 @@ struct statx {
        /* File offset alignment for direct I/O reads */
        __u32   stx_dio_read_offset_align;
 
-       /* 0xb8 */
-       __u64   __spare3[9];    /* Spare space for future expansion */
+       /* Optimised max atomic write unit in bytes */
+       __u32   stx_atomic_write_unit_max_opt;
+       __u32   __spare2[1];
+
+       /* 0xc0 */
+       __u64   __spare3[8];    /* Spare space for future expansion */
 
        /* 0x100 */
 };