summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-01-05 17:45:12 -0800
committerDarrick J. Wong <djwong@kernel.org>2021-03-25 17:08:28 -0700
commit22800c2a085b4c30e834366c560d43d77fde72d1 (patch)
tree9bb668f73fc71e7e37ba716bf6980c30a5d038da
parentf5567ff837fd0d41e437915547ba30445a0944e1 (diff)
xfs: allow xfs_swap_range to use older extent swap algorithms
If userspace permits non-atomic swap operations, use the older code paths to implement the same functionality. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/xfs_bmap_util.c4
-rw-r--r--fs/xfs/xfs_bmap_util.h4
-rw-r--r--fs/xfs/xfs_xchgrange.c66
3 files changed, 66 insertions, 8 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 9ba667ec679f..216a3e2e0404 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1256,7 +1256,7 @@ out_trans_cancel:
* reject and log the attempt. basically we are putting the responsibility on
* userspace to get this right.
*/
-static int
+int
xfs_swap_extents_check_format(
struct xfs_inode *ip, /* target inode */
struct xfs_inode *tip) /* tmp inode */
@@ -1398,7 +1398,7 @@ xfs_swap_change_owner(
}
/* Swap the extents of two files by swapping data forks. */
-STATIC int
+int
xfs_swap_extent_forks(
struct xfs_trans **tpp,
struct xfs_swapext_req *req)
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index 9f993168b55b..de3173e64f47 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -69,6 +69,10 @@ int xfs_free_eofblocks(struct xfs_inode *ip);
int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
struct xfs_swapext *sx);
+struct xfs_swapext_req;
+int xfs_swap_extent_forks(struct xfs_trans **tpp, struct xfs_swapext_req *req);
+int xfs_swap_extents_check_format(struct xfs_inode *ip, struct xfs_inode *tip);
+
xfs_daddr_t xfs_fsb_to_db(struct xfs_inode *ip, xfs_fsblock_t fsb);
xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count);
diff --git a/fs/xfs/xfs_xchgrange.c b/fs/xfs/xfs_xchgrange.c
index 877ef9f3eb64..ef74965198c6 100644
--- a/fs/xfs/xfs_xchgrange.c
+++ b/fs/xfs/xfs_xchgrange.c
@@ -259,6 +259,26 @@ err:
return error;
}
+/* Decide if we can use the old data fork exchange code. */
+static inline bool
+xfs_xchg_use_forkswap(
+ const struct file_xchg_range *fxr,
+ struct xfs_inode *ip1,
+ struct xfs_inode *ip2)
+{
+ return (fxr->flags & FILE_XCHG_RANGE_NONATOMIC) &&
+ (fxr->flags & FILE_XCHG_RANGE_FULL_FILES) &&
+ !(fxr->flags & FILE_XCHG_RANGE_TO_EOF) &&
+ fxr->file1_offset == 0 && fxr->file2_offset == 0 &&
+ fxr->length == ip1->i_d.di_size &&
+ fxr->length == ip2->i_d.di_size;
+}
+
+enum xchg_strategy {
+ SWAPEXT = 1, /* xfs_swapext() */
+ FORKSWAP = 2, /* exchange forks */
+};
+
/* Exchange the contents of two files. */
int
xfs_xchg_range(
@@ -279,12 +299,9 @@ xfs_xchg_range(
unsigned int qretry;
bool retried = false;
bool use_atomic = false;
+ enum xchg_strategy strategy;
int error;
- /* We don't support whole-fork swapping yet. */
- if (!xfs_sb_version_canatomicswap(&mp->m_sb))
- return -EOPNOTSUPP;
-
if (fxr->flags & FILE_XCHG_RANGE_TO_EOF)
req.flags |= XFS_SWAPEXT_SET_SIZES;
if (fxr->flags & FILE_XCHG_RANGE_SKIP_FILE1_HOLES)
@@ -374,6 +391,41 @@ retry:
if (error)
goto out_trans_cancel;
+ if (use_atomic || xfs_sb_version_hasreflink(&mp->m_sb) ||
+ xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+ /*
+ * xfs_swapext() uses deferred bmap log intent items to swap
+ * extents between file forks. If the atomic log swap feature
+ * is enabled, it will also use swapext log intent items to
+ * restart the operation in case of failure.
+ *
+ * This means that we can use it if we previously obtained
+ * permission from the log to use log-assisted atomic extent
+ * swapping; or if the fs supports rmap or reflink and the
+ * user said NONATOMIC.
+ */
+ strategy = SWAPEXT;
+ } else if (xfs_xchg_use_forkswap(fxr, ip1, ip2)) {
+ /*
+ * Exchange the file contents by using the old bmap fork
+ * exchange code, if we're a defrag tool doing a full file
+ * swap.
+ */
+ strategy = FORKSWAP;
+
+ error = xfs_swap_extents_check_format(ip2, ip1);
+ if (error) {
+ xfs_notice(mp,
+ "%s: inode 0x%llx format is incompatible for exchanging.",
+ __func__, ip2->i_ino);
+ goto out_trans_cancel;
+ }
+ } else {
+ /* We cannot exchange the file contents. */
+ error = -EOPNOTSUPP;
+ goto out_trans_cancel;
+ }
+
/* If we got this far on a dry run, all parameters are ok. */
if (fxr->flags & FILE_XCHG_RANGE_DRY_RUN)
goto out_trans_cancel;
@@ -395,8 +447,10 @@ retry:
xfs_trans_ichgtime(tp, ip2,
XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
- /* Exchange the file contents by swapping the block mappings. */
- error = xfs_swapext(&tp, &req);
+ if (strategy == SWAPEXT)
+ error = xfs_swapext(&tp, &req);
+ else
+ error = xfs_swap_extent_forks(&tp, &req);
if (error)
goto out_trans_cancel;