提交 2c845f5a 编写于 作者: B Brian Foster 提交者: Dave Chinner

xfs: track collapse via file offset rather than extent index

The collapse range implementation uses a transaction per extent shift.
The progress of the overall operation is tracked via the current extent
index of the in-core extent list. This is racy because the ilock must be
dropped and reacquired for each transaction according to locking and log
reservation rules. Therefore, writeback to prior regions of the file is
possible and can change the extent count. This changes the extent to
which the current index refers and causes the collapse to fail mid
operation. To avoid this problem, the entire file is currently written
back before the collapse operation starts.

To eliminate the need to flush the entire file, use the file offset
(fsb) to track the progress of the overall extent shift operation rather
than the extent index. Modify xfs_bmap_shift_extents() to
unconditionally convert the start_fsb parameter to an extent index and
return the file offset of the extent where the shift left off, if
further extents exist. The bulk of ths function can remain based on
extent index as ilock is held by the caller. xfs_collapse_file_space()
now uses the fsb output as the starting point for the subsequent shift.
Signed-off-by: NBrian Foster <bfoster@redhat.com>
Reviewed-by: NDave Chinner <dchinner@redhat.com>
Signed-off-by: NDave Chinner <david@fromorbit.com>
上级 0d085a52
...@@ -5406,20 +5406,21 @@ xfs_bunmapi( ...@@ -5406,20 +5406,21 @@ xfs_bunmapi(
/* /*
* Shift extent records to the left to cover a hole. * Shift extent records to the left to cover a hole.
* *
* The maximum number of extents to be shifted in a single operation * The maximum number of extents to be shifted in a single operation is
* is @num_exts, and @current_ext keeps track of the current extent * @num_exts. @start_fsb specifies the file offset to start the shift and the
* index we have shifted. @offset_shift_fsb is the length by which each * file offset where we've left off is returned in @next_fsb. @offset_shift_fsb
* extent is shifted. If there is no hole to shift the extents * is the length by which each extent is shifted. If there is no hole to shift
* into, this will be considered invalid operation and we abort immediately. * the extents into, this will be considered invalid operation and we abort
* immediately.
*/ */
int int
xfs_bmap_shift_extents( xfs_bmap_shift_extents(
struct xfs_trans *tp, struct xfs_trans *tp,
struct xfs_inode *ip, struct xfs_inode *ip,
int *done,
xfs_fileoff_t start_fsb, xfs_fileoff_t start_fsb,
xfs_fileoff_t offset_shift_fsb, xfs_fileoff_t offset_shift_fsb,
xfs_extnum_t *current_ext, int *done,
xfs_fileoff_t *next_fsb,
xfs_fsblock_t *firstblock, xfs_fsblock_t *firstblock,
struct xfs_bmap_free *flist, struct xfs_bmap_free *flist,
int num_exts) int num_exts)
...@@ -5431,6 +5432,7 @@ xfs_bmap_shift_extents( ...@@ -5431,6 +5432,7 @@ xfs_bmap_shift_extents(
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp; struct xfs_ifork *ifp;
xfs_extnum_t nexts = 0; xfs_extnum_t nexts = 0;
xfs_extnum_t current_ext;
xfs_fileoff_t startoff; xfs_fileoff_t startoff;
int error = 0; int error = 0;
int i; int i;
...@@ -5451,7 +5453,8 @@ xfs_bmap_shift_extents( ...@@ -5451,7 +5453,8 @@ xfs_bmap_shift_extents(
if (XFS_FORCED_SHUTDOWN(mp)) if (XFS_FORCED_SHUTDOWN(mp))
return -EIO; return -EIO;
ASSERT(current_ext != NULL); ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
ifp = XFS_IFORK_PTR(ip, whichfork); ifp = XFS_IFORK_PTR(ip, whichfork);
if (!(ifp->if_flags & XFS_IFEXTENTS)) { if (!(ifp->if_flags & XFS_IFEXTENTS)) {
...@@ -5462,20 +5465,18 @@ xfs_bmap_shift_extents( ...@@ -5462,20 +5465,18 @@ xfs_bmap_shift_extents(
} }
/* /*
* If *current_ext is 0, we would need to lookup the extent * Look up the extent index for the fsb where we start shifting. We can
* from where we would start shifting and store it in gotp. * henceforth iterate with current_ext as extent list changes are locked
* out via ilock.
*
* gotp can be null in 2 cases: 1) if there are no extents or 2)
* start_fsb lies in a hole beyond which there are no extents. Either
* way, we are done.
*/ */
if (!*current_ext) { gotp = xfs_iext_bno_to_ext(ifp, start_fsb, &current_ext);
gotp = xfs_iext_bno_to_ext(ifp, start_fsb, current_ext); if (!gotp) {
/* *done = 1;
* gotp can be null in 2 cases: 1) if there are no extents return 0;
* or 2) start_fsb lies in a hole beyond which there are
* no extents. Either way, we are done.
*/
if (!gotp) {
*done = 1;
return 0;
}
} }
if (ifp->if_flags & XFS_IFBROOT) { if (ifp->if_flags & XFS_IFBROOT) {
...@@ -5487,36 +5488,32 @@ xfs_bmap_shift_extents( ...@@ -5487,36 +5488,32 @@ xfs_bmap_shift_extents(
/* /*
* There may be delalloc extents in the data fork before the range we * There may be delalloc extents in the data fork before the range we
* are collapsing out, so we cannot * are collapsing out, so we cannot use the count of real extents here.
* use the count of real extents here. Instead we have to calculate it * Instead we have to calculate it from the incore fork.
* from the incore fork.
*/ */
total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t); total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
while (nexts++ < num_exts && *current_ext < total_extents) { while (nexts++ < num_exts && current_ext < total_extents) {
gotp = xfs_iext_get_ext(ifp, *current_ext); gotp = xfs_iext_get_ext(ifp, current_ext);
xfs_bmbt_get_all(gotp, &got); xfs_bmbt_get_all(gotp, &got);
startoff = got.br_startoff - offset_shift_fsb; startoff = got.br_startoff - offset_shift_fsb;
/* /*
* Before shifting extent into hole, make sure that the hole * Before shifting extent into hole, make sure that the hole is
* is large enough to accomodate the shift. * large enough to accommodate the shift.
*/ */
if (*current_ext) { if (current_ext > 0) {
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, xfs_bmbt_get_all(xfs_iext_get_ext(ifp, current_ext - 1),
*current_ext - 1), &left); &left);
if (startoff < left.br_startoff + left.br_blockcount) if (startoff < left.br_startoff + left.br_blockcount)
error = -EINVAL; error = -EINVAL;
} else if (offset_shift_fsb > got.br_startoff) { } else if (offset_shift_fsb > got.br_startoff) {
/* /*
* When first extent is shifted, offset_shift_fsb * When first extent is shifted, offset_shift_fsb should
* should be less than the stating offset of * be less than the stating offset of the first extent.
* the first extent.
*/ */
error = -EINVAL; error = -EINVAL;
} }
if (error) if (error)
goto del_cursor; goto del_cursor;
...@@ -5531,7 +5528,7 @@ xfs_bmap_shift_extents( ...@@ -5531,7 +5528,7 @@ xfs_bmap_shift_extents(
} }
/* Check if we can merge 2 adjacent extents */ /* Check if we can merge 2 adjacent extents */
if (*current_ext && if (current_ext &&
left.br_startoff + left.br_blockcount == startoff && left.br_startoff + left.br_blockcount == startoff &&
left.br_startblock + left.br_blockcount == left.br_startblock + left.br_blockcount ==
got.br_startblock && got.br_startblock &&
...@@ -5539,7 +5536,7 @@ xfs_bmap_shift_extents( ...@@ -5539,7 +5536,7 @@ xfs_bmap_shift_extents(
left.br_blockcount + got.br_blockcount <= MAXEXTLEN) { left.br_blockcount + got.br_blockcount <= MAXEXTLEN) {
blockcount = left.br_blockcount + blockcount = left.br_blockcount +
got.br_blockcount; got.br_blockcount;
xfs_iext_remove(ip, *current_ext, 1, 0); xfs_iext_remove(ip, current_ext, 1, 0);
logflags |= XFS_ILOG_CORE; logflags |= XFS_ILOG_CORE;
if (cur) { if (cur) {
error = xfs_btree_delete(cur, &i); error = xfs_btree_delete(cur, &i);
...@@ -5551,7 +5548,7 @@ xfs_bmap_shift_extents( ...@@ -5551,7 +5548,7 @@ xfs_bmap_shift_extents(
} }
XFS_IFORK_NEXT_SET(ip, whichfork, XFS_IFORK_NEXT_SET(ip, whichfork,
XFS_IFORK_NEXTENTS(ip, whichfork) - 1); XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
gotp = xfs_iext_get_ext(ifp, --*current_ext); gotp = xfs_iext_get_ext(ifp, --current_ext);
xfs_bmbt_get_all(gotp, &got); xfs_bmbt_get_all(gotp, &got);
/* Make cursor point to the extent we will update */ /* Make cursor point to the extent we will update */
...@@ -5585,13 +5582,18 @@ xfs_bmap_shift_extents( ...@@ -5585,13 +5582,18 @@ xfs_bmap_shift_extents(
logflags |= XFS_ILOG_DEXT; logflags |= XFS_ILOG_DEXT;
} }
(*current_ext)++; current_ext++;
total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t); total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
} }
/* Check if we are done */ /* Check if we are done */
if (*current_ext == total_extents) if (current_ext == total_extents)
*done = 1; *done = 1;
else if (next_fsb) {
gotp = xfs_iext_get_ext(ifp, current_ext);
xfs_bmbt_get_all(gotp, &got);
*next_fsb = got.br_startoff;
}
del_cursor: del_cursor:
if (cur) if (cur)
...@@ -5600,5 +5602,6 @@ xfs_bmap_shift_extents( ...@@ -5600,5 +5602,6 @@ xfs_bmap_shift_extents(
if (logflags) if (logflags)
xfs_trans_log_inode(tp, ip, logflags); xfs_trans_log_inode(tp, ip, logflags);
return error; return error;
} }
...@@ -178,9 +178,8 @@ int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx, ...@@ -178,9 +178,8 @@ int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx,
xfs_extnum_t num); xfs_extnum_t num);
uint xfs_default_attroffset(struct xfs_inode *ip); uint xfs_default_attroffset(struct xfs_inode *ip);
int xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip, int xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip,
int *done, xfs_fileoff_t start_fsb, xfs_fileoff_t start_fsb, xfs_fileoff_t offset_shift_fsb,
xfs_fileoff_t offset_shift_fsb, xfs_extnum_t *current_ext, int *done, xfs_fileoff_t *next_fsb, xfs_fsblock_t *firstblock,
xfs_fsblock_t *firstblock, struct xfs_bmap_free *flist, struct xfs_bmap_free *flist, int num_exts);
int num_exts);
#endif /* __XFS_BMAP_H__ */ #endif /* __XFS_BMAP_H__ */
...@@ -1456,18 +1456,18 @@ xfs_collapse_file_space( ...@@ -1456,18 +1456,18 @@ xfs_collapse_file_space(
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp; struct xfs_trans *tp;
int error; int error;
xfs_extnum_t current_ext = 0;
struct xfs_bmap_free free_list; struct xfs_bmap_free free_list;
xfs_fsblock_t first_block; xfs_fsblock_t first_block;
int committed; int committed;
xfs_fileoff_t start_fsb; xfs_fileoff_t start_fsb;
xfs_fileoff_t next_fsb;
xfs_fileoff_t shift_fsb; xfs_fileoff_t shift_fsb;
ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
trace_xfs_collapse_file_space(ip); trace_xfs_collapse_file_space(ip);
start_fsb = XFS_B_TO_FSB(mp, offset + len); next_fsb = XFS_B_TO_FSB(mp, offset + len);
shift_fsb = XFS_B_TO_FSB(mp, len); shift_fsb = XFS_B_TO_FSB(mp, len);
/* /*
...@@ -1525,10 +1525,10 @@ xfs_collapse_file_space( ...@@ -1525,10 +1525,10 @@ xfs_collapse_file_space(
* We are using the write transaction in which max 2 bmbt * We are using the write transaction in which max 2 bmbt
* updates are allowed * updates are allowed
*/ */
error = xfs_bmap_shift_extents(tp, ip, &done, start_fsb, start_fsb = next_fsb;
shift_fsb, &current_ext, error = xfs_bmap_shift_extents(tp, ip, start_fsb, shift_fsb,
&first_block, &free_list, &done, &next_fsb, &first_block, &free_list,
XFS_BMAP_MAX_SHIFT_EXTENTS); XFS_BMAP_MAX_SHIFT_EXTENTS);
if (error) if (error)
goto out; goto out;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册