xfs: support removing extents from CoW fork

Create a helper method to remove extents from the CoW fork without
any of the side effects (rmapbt/bmbt updates) of the regular extent
deletion routine.  We'll eventually use this to clear out the CoW fork
during ioend processing.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Darrick J. Wong 2016-10-03 09:11:35 -07:00
parent ef4736678f
commit 4862cfe825
2 changed files with 171 additions and 0 deletions

View File

@ -4908,6 +4908,7 @@ xfs_bmap_del_extent(
/*
* Matches the whole extent. Delete the entry.
*/
trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
xfs_iext_remove(ip, *idx, 1,
whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0);
--*idx;
@ -5124,6 +5125,175 @@ xfs_bmap_del_extent(
return error;
}
/* Remove an extent from the CoW fork. Similar to xfs_bmap_del_extent. */
int
xfs_bunmapi_cow(
struct xfs_inode *ip,
struct xfs_bmbt_irec *del)
{
xfs_filblks_t da_new;
xfs_filblks_t da_old;
xfs_fsblock_t del_endblock = 0;
xfs_fileoff_t del_endoff;
int delay;
struct xfs_bmbt_rec_host *ep;
int error;
struct xfs_bmbt_irec got;
xfs_fileoff_t got_endoff;
struct xfs_ifork *ifp;
struct xfs_mount *mp;
xfs_filblks_t nblks;
struct xfs_bmbt_irec new;
/* REFERENCED */
uint qfield;
xfs_filblks_t temp;
xfs_filblks_t temp2;
int state = BMAP_COWFORK;
int eof;
xfs_extnum_t eidx;
mp = ip->i_mount;
XFS_STATS_INC(mp, xs_del_exlist);
ep = xfs_bmap_search_extents(ip, del->br_startoff, XFS_COW_FORK, &eof,
&eidx, &got, &new);
ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); ifp = ifp;
ASSERT((eidx >= 0) && (eidx < ifp->if_bytes /
(uint)sizeof(xfs_bmbt_rec_t)));
ASSERT(del->br_blockcount > 0);
ASSERT(got.br_startoff <= del->br_startoff);
del_endoff = del->br_startoff + del->br_blockcount;
got_endoff = got.br_startoff + got.br_blockcount;
ASSERT(got_endoff >= del_endoff);
delay = isnullstartblock(got.br_startblock);
ASSERT(isnullstartblock(del->br_startblock) == delay);
qfield = 0;
error = 0;
/*
* If deleting a real allocation, must free up the disk space.
*/
if (!delay) {
nblks = del->br_blockcount;
qfield = XFS_TRANS_DQ_BCOUNT;
/*
* Set up del_endblock and cur for later.
*/
del_endblock = del->br_startblock + del->br_blockcount;
da_old = da_new = 0;
} else {
da_old = startblockval(got.br_startblock);
da_new = 0;
nblks = 0;
}
qfield = qfield;
nblks = nblks;
/*
* Set flag value to use in switch statement.
* Left-contig is 2, right-contig is 1.
*/
switch (((got.br_startoff == del->br_startoff) << 1) |
(got_endoff == del_endoff)) {
case 3:
/*
* Matches the whole extent. Delete the entry.
*/
xfs_iext_remove(ip, eidx, 1, BMAP_COWFORK);
--eidx;
break;
case 2:
/*
* Deleting the first part of the extent.
*/
trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
xfs_bmbt_set_startoff(ep, del_endoff);
temp = got.br_blockcount - del->br_blockcount;
xfs_bmbt_set_blockcount(ep, temp);
if (delay) {
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
da_old);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
da_new = temp;
break;
}
xfs_bmbt_set_startblock(ep, del_endblock);
trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
break;
case 1:
/*
* Deleting the last part of the extent.
*/
temp = got.br_blockcount - del->br_blockcount;
trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep, temp);
if (delay) {
temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
da_old);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
da_new = temp;
break;
}
trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
break;
case 0:
/*
* Deleting the middle of the extent.
*/
temp = del->br_startoff - got.br_startoff;
trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
xfs_bmbt_set_blockcount(ep, temp);
new.br_startoff = del_endoff;
temp2 = got_endoff - del_endoff;
new.br_blockcount = temp2;
new.br_state = got.br_state;
if (!delay) {
new.br_startblock = del_endblock;
} else {
temp = xfs_bmap_worst_indlen(ip, temp);
xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
temp2 = xfs_bmap_worst_indlen(ip, temp2);
new.br_startblock = nullstartblock((int)temp2);
da_new = temp + temp2;
while (da_new > da_old) {
if (temp) {
temp--;
da_new--;
xfs_bmbt_set_startblock(ep,
nullstartblock((int)temp));
}
if (da_new == da_old)
break;
if (temp2) {
temp2--;
da_new--;
new.br_startblock =
nullstartblock((int)temp2);
}
}
}
trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
xfs_iext_insert(ip, eidx + 1, 1, &new, state);
++eidx;
break;
}
/*
* Account for change in delayed indirect blocks.
* Nothing to do for disk quota accounting here.
*/
ASSERT(da_old >= da_new);
if (da_old > da_new)
xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new), false);
return error;
}
/*
* Unmap (remove) blocks from a file.
* If nexts is nonzero then the number of extents to remove is limited to

View File

@ -221,6 +221,7 @@ int xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
xfs_fileoff_t bno, xfs_filblks_t len, int flags,
xfs_extnum_t nexts, xfs_fsblock_t *firstblock,
struct xfs_defer_ops *dfops, int *done);
int xfs_bunmapi_cow(struct xfs_inode *ip, struct xfs_bmbt_irec *del);
int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx,
xfs_extnum_t num);
uint xfs_default_attroffset(struct xfs_inode *ip);