Currently only HW which can write at least 1x block is supported. For supporting atomic writes > 1x block, a CoW-based method will also be used and this will not be resticted to using HW which can write >= 1x block. However for deciding if HW-based atomic writes can be used, we need to start adding checks for write length < HW min, which complicates the code. Indeed, a statx field similar to unit_max_opt should also be added for this minimum, which is undesirable. HW which can only write > 1x blocks would be uncommon and quite weird, so let's just not support it. Signed-off-by: John Garry <john.g.garry@xxxxxxxxxx> --- fs/xfs/xfs_inode.h | 17 ++++++++--------- fs/xfs/xfs_mount.c | 14 ++++++++++++++ fs/xfs/xfs_mount.h | 4 ++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index cff643cd03fc..725cd7c16a6e 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -355,20 +355,19 @@ static inline bool xfs_inode_has_bigrtalloc(const struct xfs_inode *ip) #define xfs_inode_buftarg(ip) \ (XFS_IS_REALTIME_INODE(ip) ? \ (ip)->i_mount->m_rtdev_targp : (ip)->i_mount->m_ddev_targp) +/* + * Return max atomic write unit for a given inode. + */ +#define xfs_inode_hw_atomicwrite_max(ip) \ + (XFS_IS_REALTIME_INODE(ip) ? \ + (ip)->i_mount->m_rt_awu_hw_max : \ + (ip)->i_mount->m_dd_awu_hw_max) static inline bool xfs_inode_can_hw_atomicwrite( struct xfs_inode *ip) { - struct xfs_mount *mp = ip->i_mount; - struct xfs_buftarg *target = xfs_inode_buftarg(ip); - - if (mp->m_sb.sb_blocksize < target->bt_bdev_awu_min) - return false; - if (mp->m_sb.sb_blocksize > target->bt_bdev_awu_max) - return false; - - return true; + return xfs_inode_hw_atomicwrite_max(ip); } /* diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 00b53f479ece..ee68c026e6cd 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1082,6 +1082,20 @@ xfs_mountfs( xfs_zone_gc_start(mp); } + /* + * Set atomic write unit max for mp. Ignore devices which cannot atomic + * a single block, as they would be uncommon and more difficult to + * support. + */ + if (mp->m_ddev_targp->bt_bdev_awu_min <= mp->m_sb.sb_blocksize && + mp->m_ddev_targp->bt_bdev_awu_max >= mp->m_sb.sb_blocksize) + mp->m_dd_awu_hw_max = mp->m_ddev_targp->bt_bdev_awu_max; + + if (mp->m_rtdev_targp && + mp->m_rtdev_targp->bt_bdev_awu_min <= mp->m_sb.sb_blocksize && + mp->m_rtdev_targp->bt_bdev_awu_max >= mp->m_sb.sb_blocksize) + mp->m_rt_awu_hw_max = mp->m_rtdev_targp->bt_bdev_awu_max; + return 0; out_agresv: diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index e5192c12e7ac..2819e160f0e9 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -231,6 +231,10 @@ typedef struct xfs_mount { unsigned int m_max_open_zones; unsigned int m_zonegc_low_space; + /* ddev and rtdev HW max atomic write size */ + unsigned int m_dd_awu_hw_max; + unsigned int m_rt_awu_hw_max; + /* * Bitsets of per-fs metadata that have been checked and/or are sick. * Callers must hold m_sb_lock to access these two fields. -- 2.31.1