@@ -814,9 +814,13 @@ struct xfs_swap_extent {
/* Do not swap any part of the range where file1's mapping is a hole. */
#define XFS_SWAP_EXTENT_SKIP_FILE1_HOLES (1ULL << 2)
+/* Try to convert inode2 from block to short format at the end, if possible. */
+#define XFS_SWAP_EXTENT_INO2_SHORTFORM (1ULL << 3)
+
#define XFS_SWAP_EXTENT_FLAGS (XFS_SWAP_EXTENT_ATTR_FORK | \
XFS_SWAP_EXTENT_SET_SIZES | \
- XFS_SWAP_EXTENT_SKIP_FILE1_HOLES)
+ XFS_SWAP_EXTENT_SKIP_FILE1_HOLES | \
+ XFS_SWAP_EXTENT_INO2_SHORTFORM)
/* This is the structure used to lay out an sxi log item in the log. */
struct xfs_sxi_log_format {
@@ -22,6 +22,9 @@
#include "xfs_trans_space.h"
#include "xfs_errortag.h"
#include "xfs_error.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr_leaf.h"
/* Information to help us reset reflink flag / CoW fork state after a swap. */
@@ -196,12 +199,46 @@ xfs_swapext_update_size(
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
}
+/* Convert inode2's leaf attr fork back to shortform, if possible.. */
+STATIC int
+xfs_swapext_attr_to_shortform2(
+ struct xfs_trans *tp,
+ struct xfs_swapext_intent *sxi)
+{
+ struct xfs_da_args args = {
+ .dp = sxi->sxi_ip2,
+ .geo = tp->t_mountp->m_attr_geo,
+ .whichfork = XFS_ATTR_FORK,
+ .trans = tp,
+ };
+ struct xfs_buf *bp;
+ int forkoff;
+ int error;
+
+ if (!xfs_bmap_one_block(sxi->sxi_ip2, XFS_ATTR_FORK))
+ return 0;
+
+ error = xfs_attr3_leaf_read(tp, sxi->sxi_ip2, 0, &bp);
+ if (error)
+ return error;
+
+ forkoff = xfs_attr_shortform_allfit(bp, sxi->sxi_ip2);
+ if (forkoff == 0)
+ return 0;
+
+ return xfs_attr3_leaf_to_shortform(bp, &args, forkoff);
+}
+
+/* Mask of all flags that require post-processing of file2. */
+#define XFS_SWAP_EXTENT_POST_PROCESSING (XFS_SWAP_EXTENT_INO2_SHORTFORM)
+
/* Do we have more work to do to finish this operation? */
bool
xfs_swapext_has_more_work(
struct xfs_swapext_intent *sxi)
{
- return sxi->sxi_blockcount > 0;
+ return sxi->sxi_blockcount > 0 ||
+ (sxi->sxi_flags & XFS_SWAP_EXTENT_POST_PROCESSING);
}
/* Check all extents to make sure we can actually swap them. */
@@ -273,12 +310,23 @@ xfs_swapext_finish_one(
int whichfork;
int nimaps;
int bmap_flags;
- int error;
+ int error = 0;
whichfork = (sxi->sxi_flags & XFS_SWAP_EXTENT_ATTR_FORK) ?
XFS_ATTR_FORK : XFS_DATA_FORK;
bmap_flags = xfs_bmapi_aflag(whichfork);
+ /* Do any post-processing work that we requires a transaction roll. */
+ if (sxi->sxi_blockcount == 0) {
+ if (sxi->sxi_flags & XFS_SWAP_EXTENT_INO2_SHORTFORM) {
+ if (sxi->sxi_flags & XFS_SWAP_EXTENT_ATTR_FORK)
+ error = xfs_swapext_attr_to_shortform2(tp, sxi);
+ sxi->sxi_flags &= ~XFS_SWAP_EXTENT_INO2_SHORTFORM;
+ return error;
+ }
+ return 0;
+ }
+
while (sxi->sxi_blockcount > 0) {
/* Read extent from the first file */
nimaps = 1;
@@ -419,7 +467,7 @@ xfs_swapext_finish_one(
}
/* Estimate the bmbt and rmapbt overhead required to exchange extents. */
-static int
+int
xfs_swapext_estimate_overhead(
const struct xfs_swapext_req *req,
struct xfs_swapext_res *res)
@@ -838,6 +886,8 @@ xfs_swapext_init_intent(
}
if (req->flags & XFS_SWAPEXT_SKIP_FILE1_HOLES)
sxi->sxi_flags |= XFS_SWAP_EXTENT_SKIP_FILE1_HOLES;
+ if (req->flags & XFS_SWAPEXT_INO2_SHORTFORM)
+ sxi->sxi_flags |= XFS_SWAP_EXTENT_INO2_SHORTFORM;
sxi->sxi_ip1 = req->ip1;
sxi->sxi_ip2 = req->ip2;
sxi->sxi_startoff1 = req->startoff1;
@@ -41,6 +41,9 @@ struct xfs_swapext_intent {
/* Do not swap any part of the range where file1's mapping is a hole. */
#define XFS_SWAPEXT_SKIP_FILE1_HOLES (1U << 1)
+/* Try to convert inode2's fork to local format, if possible. */
+#define XFS_SWAPEXT_INO2_SHORTFORM (1U << 2)
+
/* Parameters for a swapext request. */
struct xfs_swapext_req {
struct xfs_inode *ip1;
@@ -68,6 +71,8 @@ unsigned int xfs_swapext_reflink_prep(const struct xfs_swapext_req *req);
void xfs_swapext_reflink_finish(struct xfs_trans *tp,
const struct xfs_swapext_req *req, unsigned int reflink_state);
+int xfs_swapext_estimate_overhead(const struct xfs_swapext_req *req,
+ struct xfs_swapext_res *res);
int xfs_swapext_estimate(const struct xfs_swapext_req *req,
struct xfs_swapext_res *res);
@@ -3954,7 +3954,8 @@ DEFINE_EOFBLOCKS_EVENT(xfs_inodegc_free_space);
#define XFS_SWAPEXT_STRINGS \
{ XFS_SWAPEXT_SET_SIZES, "SETSIZES" }, \
- { XFS_SWAPEXT_SKIP_FILE1_HOLES, "SKIP_FILE1_HOLES" }
+ { XFS_SWAPEXT_SKIP_FILE1_HOLES, "SKIP_FILE1_HOLES" }, \
+ { XFS_SWAPEXT_INO2_SHORTFORM, "INO2_SHORTFORM" }
TRACE_EVENT(xfs_swapext_estimate,
TP_PROTO(const struct xfs_swapext_req *req,
@@ -4010,7 +4011,8 @@ TRACE_EVENT(xfs_swapext_estimate,
#define XFS_SWAP_EXTENT_STRINGS \
{ XFS_SWAP_EXTENT_ATTR_FORK, "ATTRFORK" }, \
{ XFS_SWAP_EXTENT_SET_SIZES, "SETSIZES" }, \
- { XFS_SWAP_EXTENT_SKIP_FILE1_HOLES, "SKIP_FILE1_HOLES" }
+ { XFS_SWAP_EXTENT_SKIP_FILE1_HOLES, "SKIP_FILE1_HOLES" }, \
+ { XFS_SWAP_EXTENT_INO2_SHORTFORM, "INO2_SHORTFORM" }
TRACE_EVENT(xfs_swapext_defer,
TP_PROTO(struct xfs_mount *mp, const struct xfs_swapext_intent *sxi),