summaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_reflink.c
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2016-10-03 09:11:39 -0700
committerDarrick J. Wong <darrick.wong@oracle.com>2016-10-05 16:26:05 -0700
commit174edb0e46e520230791a1a894397b7c824cefc4 (patch)
treec15257ff7f762c89bc3ddebe5bba23538a57cbcf /fs/xfs/xfs_reflink.c
parent5e7e605c4d1b5229c7df8a71798df00a4300428b (diff)
downloadlinux-174edb0e46e520230791a1a894397b7c824cefc4.tar.bz2
xfs: store in-progress CoW allocations in the refcount btree
Due to the way the CoW algorithm in XFS works, there's an interval during which blocks allocated to handle a CoW can be lost -- if the FS goes down after the blocks are allocated but before the block remapping takes place. This is exacerbated by the cowextsz hint -- allocated reservations can sit around for a while, waiting to get used. Since the refcount btree doesn't normally store records with refcount of 1, we can use it to record these in-progress extents. In-progress blocks cannot be shared because they're not user-visible, so there shouldn't be any conflicts with other programs. This is a better solution than holding EFIs during writeback because (a) EFIs can't be relogged currently, (b) even if they could, EFIs are bound by available log space, which puts an unnecessary upper bound on how much CoW we can have in flight, and (c) we already have a mechanism to track blocks. At mount time, read the refcount records and free anything we find with a refcount of 1 because those were in-progress when the FS went down. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
-rw-r--r--fs/xfs/xfs_reflink.c37
1 files changed, 37 insertions, 0 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index a30be03395fb..0aac26208a82 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -40,6 +40,7 @@
#include "xfs_log.h"
#include "xfs_icache.h"
#include "xfs_pnfs.h"
+#include "xfs_btree.h"
#include "xfs_refcount_btree.h"
#include "xfs_refcount.h"
#include "xfs_bmap_btree.h"
@@ -563,6 +564,13 @@ xfs_reflink_cancel_cow_blocks(
xfs_trans_ijoin(*tpp, ip, 0);
xfs_defer_init(&dfops, &firstfsb);
+ /* Free the CoW orphan record. */
+ error = xfs_refcount_free_cow_extent(ip->i_mount,
+ &dfops, irec.br_startblock,
+ irec.br_blockcount);
+ if (error)
+ break;
+
xfs_bmap_add_free(ip->i_mount, &dfops,
irec.br_startblock, irec.br_blockcount,
NULL);
@@ -719,6 +727,13 @@ xfs_reflink_end_cow(
irec.br_blockcount = rlen;
trace_xfs_reflink_cow_remap_piece(ip, &uirec);
+ /* Free the CoW orphan record. */
+ error = xfs_refcount_free_cow_extent(tp->t_mountp,
+ &dfops, uirec.br_startblock,
+ uirec.br_blockcount);
+ if (error)
+ goto out_defer;
+
/* Map the new blocks into the data fork. */
error = xfs_bmap_map_extent(tp->t_mountp, &dfops,
ip, &uirec);
@@ -755,3 +770,25 @@ out:
trace_xfs_reflink_end_cow_error(ip, error, _RET_IP_);
return error;
}
+
+/*
+ * Free leftover CoW reservations that didn't get cleaned out.
+ */
+int
+xfs_reflink_recover_cow(
+ struct xfs_mount *mp)
+{
+ xfs_agnumber_t agno;
+ int error = 0;
+
+ if (!xfs_sb_version_hasreflink(&mp->m_sb))
+ return 0;
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ error = xfs_refcount_recover_cow_leftovers(mp, agno);
+ if (error)
+ break;
+ }
+
+ return error;
+}