From: Darrick J. Wong <djwong@xxxxxxxxxx> Now that bmap items support the realtime device, we can add the necessary pieces to the atomic extent swapping code to support such things. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/xfs/libxfs/xfs_format.h | 7 ++-- fs/xfs/libxfs/xfs_swapext.c | 67 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index e81a7b12a0e3..15d967414500 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -611,13 +611,12 @@ static inline bool xfs_sb_version_needsrepair(struct xfs_sb *sbp) /* * Decide if this filesystem can use log-assisted ("atomic") extent swapping. * The atomic swap log intent items depend on the block mapping log intent - * items introduced with reflink and rmap. Realtime is not supported yet. + * items introduced with reflink and rmap. */ static inline bool xfs_sb_version_canatomicswap(struct xfs_sb *sbp) { - return (xfs_sb_version_hasreflink(sbp) || - xfs_sb_version_hasrmapbt(sbp)) && - !xfs_sb_version_hasrealtime(sbp); + return xfs_sb_version_hasreflink(sbp) || + xfs_sb_version_hasrmapbt(sbp); } static inline bool xfs_sb_version_hasatomicswap(struct xfs_sb *sbp) diff --git a/fs/xfs/libxfs/xfs_swapext.c b/fs/xfs/libxfs/xfs_swapext.c index 41042ee05e40..995b59d86d79 100644 --- a/fs/xfs/libxfs/xfs_swapext.c +++ b/fs/xfs/libxfs/xfs_swapext.c @@ -279,7 +279,14 @@ xfs_swapext_check_extents( struct xfs_mount *mp, const struct xfs_swapext_req *req) { + struct xfs_bmbt_irec irec1, irec2; struct xfs_ifork *ifp1, *ifp2; + xfs_fileoff_t startoff1 = req->startoff1; + xfs_fileoff_t startoff2 = req->startoff2; + xfs_filblks_t blockcount = req->blockcount; + uint32_t mod; + int nimaps; + int error; /* No fork? */ ifp1 = XFS_IFORK_PTR(req->ip1, req->whichfork); @@ -292,12 +299,68 @@ xfs_swapext_check_extents( ifp2->if_format == XFS_DINODE_FMT_LOCAL) return -EINVAL; - /* We don't support realtime data forks yet. */ + /* + * There may be partially written rt extents lurking in the ranges to + * be swapped. If we support atomic swapext log intent items, we can + * guarantee that operations will always finish and never leave an rt + * extent partially mapped to two files, and can move on. If we don't + * have that coordination, we have to scan both ranges to ensure that + * there are no partially written extents. + */ if (!XFS_IS_REALTIME_INODE(req->ip1)) return 0; if (req->whichfork == XFS_ATTR_FORK) return 0; - return -EINVAL; + if (xfs_sb_version_hasatomicswap(&mp->m_sb)) + return 0; + if (mp->m_sb.sb_rextsize == 1) + return 0; + + while (blockcount > 0) { + /* Read extent from the first file */ + nimaps = 1; + error = xfs_bmapi_read(req->ip1, startoff1, blockcount, + &irec1, &nimaps, 0); + if (error) + return error; + ASSERT(nimaps == 1); + + /* Read extent from the second file */ + nimaps = 1; + error = xfs_bmapi_read(req->ip2, startoff2, + irec1.br_blockcount, &irec2, &nimaps, + 0); + if (error) + return error; + ASSERT(nimaps == 1); + + /* + * We can only swap as many blocks as the smaller of the two + * extent maps. + */ + irec1.br_blockcount = min(irec1.br_blockcount, + irec2.br_blockcount); + + /* + * Both mappings must be aligned to the realtime extent size + * if either mapping comes from the realtime volume. + */ + div_u64_rem(irec1.br_startoff, mp->m_sb.sb_rextsize, &mod); + if (mod) + return -EINVAL; + div_u64_rem(irec2.br_startoff, mp->m_sb.sb_rextsize, &mod); + if (mod) + return -EINVAL; + div_u64_rem(irec1.br_blockcount, mp->m_sb.sb_rextsize, &mod); + if (mod) + return -EINVAL; + + startoff1 += irec1.br_blockcount; + startoff2 += irec1.br_blockcount; + blockcount -= irec1.br_blockcount; + } + + return 0; } #ifdef CONFIG_XFS_QUOTA