mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 05:02:12 +00:00
xfs: remove an extent from the rmap btree
Originally-From: Dave Chinner <dchinner@redhat.com> Now that we have records in the rmap btree, we need to remove them when extents are freed. This needs to find the relevant record in the btree and remove/trim/split it accordingly. [darrick.wong@oracle.com: make rmap routines handle the enlarged keyspace] [dchinner: remove remaining unused debug printks] [darrick: fix a bug when growfs in an AG with an rmap ending at EOFS] Signed-off-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
parent
0a1b0b3855
commit
f922cd90b8
@ -133,6 +133,212 @@ xfs_rmap_get_rec(
|
||||
return xfs_rmap_btrec_to_irec(rec, irec);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the extent in the rmap btree and remove it.
|
||||
*
|
||||
* The record we find should always be an exact match for the extent that we're
|
||||
* looking for, since we insert them into the btree without modification.
|
||||
*
|
||||
* Special Case #1: when growing the filesystem, we "free" an extent when
|
||||
* growing the last AG. This extent is new space and so it is not tracked as
|
||||
* used space in the btree. The growfs code will pass in an owner of
|
||||
* XFS_RMAP_OWN_NULL to indicate that it expected that there is no owner of this
|
||||
* extent. We verify that - the extent lookup result in a record that does not
|
||||
* overlap.
|
||||
*
|
||||
* Special Case #2: EFIs do not record the owner of the extent, so when
|
||||
* recovering EFIs from the log we pass in XFS_RMAP_OWN_UNKNOWN to tell the rmap
|
||||
* btree to ignore the owner (i.e. wildcard match) so we don't trigger
|
||||
* corruption checks during log recovery.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_rmap_unmap(
|
||||
struct xfs_btree_cur *cur,
|
||||
xfs_agblock_t bno,
|
||||
xfs_extlen_t len,
|
||||
bool unwritten,
|
||||
struct xfs_owner_info *oinfo)
|
||||
{
|
||||
struct xfs_mount *mp = cur->bc_mp;
|
||||
struct xfs_rmap_irec ltrec;
|
||||
uint64_t ltoff;
|
||||
int error = 0;
|
||||
int i;
|
||||
uint64_t owner;
|
||||
uint64_t offset;
|
||||
unsigned int flags;
|
||||
bool ignore_off;
|
||||
|
||||
xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
|
||||
ignore_off = XFS_RMAP_NON_INODE_OWNER(owner) ||
|
||||
(flags & XFS_RMAP_BMBT_BLOCK);
|
||||
if (unwritten)
|
||||
flags |= XFS_RMAP_UNWRITTEN;
|
||||
trace_xfs_rmap_unmap(mp, cur->bc_private.a.agno, bno, len,
|
||||
unwritten, oinfo);
|
||||
|
||||
/*
|
||||
* We should always have a left record because there's a static record
|
||||
* for the AG headers at rm_startblock == 0 created by mkfs/growfs that
|
||||
* will not ever be removed from the tree.
|
||||
*/
|
||||
error = xfs_rmap_lookup_le(cur, bno, len, owner, offset, flags, &i);
|
||||
if (error)
|
||||
goto out_error;
|
||||
XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
|
||||
|
||||
error = xfs_rmap_get_rec(cur, <rec, &i);
|
||||
if (error)
|
||||
goto out_error;
|
||||
XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
|
||||
trace_xfs_rmap_lookup_le_range_result(cur->bc_mp,
|
||||
cur->bc_private.a.agno, ltrec.rm_startblock,
|
||||
ltrec.rm_blockcount, ltrec.rm_owner,
|
||||
ltrec.rm_offset, ltrec.rm_flags);
|
||||
ltoff = ltrec.rm_offset;
|
||||
|
||||
/*
|
||||
* For growfs, the incoming extent must be beyond the left record we
|
||||
* just found as it is new space and won't be used by anyone. This is
|
||||
* just a corruption check as we don't actually do anything with this
|
||||
* extent. Note that we need to use >= instead of > because it might
|
||||
* be the case that the "left" extent goes all the way to EOFS.
|
||||
*/
|
||||
if (owner == XFS_RMAP_OWN_NULL) {
|
||||
XFS_WANT_CORRUPTED_GOTO(mp, bno >= ltrec.rm_startblock +
|
||||
ltrec.rm_blockcount, out_error);
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
/* Make sure the unwritten flag matches. */
|
||||
XFS_WANT_CORRUPTED_GOTO(mp, (flags & XFS_RMAP_UNWRITTEN) ==
|
||||
(ltrec.rm_flags & XFS_RMAP_UNWRITTEN), out_error);
|
||||
|
||||
/* Make sure the extent we found covers the entire freeing range. */
|
||||
XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_startblock <= bno &&
|
||||
ltrec.rm_startblock + ltrec.rm_blockcount >=
|
||||
bno + len, out_error);
|
||||
|
||||
/* Make sure the owner matches what we expect to find in the tree. */
|
||||
XFS_WANT_CORRUPTED_GOTO(mp, owner == ltrec.rm_owner ||
|
||||
XFS_RMAP_NON_INODE_OWNER(owner), out_error);
|
||||
|
||||
/* Check the offset, if necessary. */
|
||||
if (!XFS_RMAP_NON_INODE_OWNER(owner)) {
|
||||
if (flags & XFS_RMAP_BMBT_BLOCK) {
|
||||
XFS_WANT_CORRUPTED_GOTO(mp,
|
||||
ltrec.rm_flags & XFS_RMAP_BMBT_BLOCK,
|
||||
out_error);
|
||||
} else {
|
||||
XFS_WANT_CORRUPTED_GOTO(mp,
|
||||
ltrec.rm_offset <= offset, out_error);
|
||||
XFS_WANT_CORRUPTED_GOTO(mp,
|
||||
ltoff + ltrec.rm_blockcount >= offset + len,
|
||||
out_error);
|
||||
}
|
||||
}
|
||||
|
||||
if (ltrec.rm_startblock == bno && ltrec.rm_blockcount == len) {
|
||||
/* exact match, simply remove the record from rmap tree */
|
||||
trace_xfs_rmap_delete(mp, cur->bc_private.a.agno,
|
||||
ltrec.rm_startblock, ltrec.rm_blockcount,
|
||||
ltrec.rm_owner, ltrec.rm_offset,
|
||||
ltrec.rm_flags);
|
||||
error = xfs_btree_delete(cur, &i);
|
||||
if (error)
|
||||
goto out_error;
|
||||
XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
|
||||
} else if (ltrec.rm_startblock == bno) {
|
||||
/*
|
||||
* overlap left hand side of extent: move the start, trim the
|
||||
* length and update the current record.
|
||||
*
|
||||
* ltbno ltlen
|
||||
* Orig: |oooooooooooooooooooo|
|
||||
* Freeing: |fffffffff|
|
||||
* Result: |rrrrrrrrrr|
|
||||
* bno len
|
||||
*/
|
||||
ltrec.rm_startblock += len;
|
||||
ltrec.rm_blockcount -= len;
|
||||
if (!ignore_off)
|
||||
ltrec.rm_offset += len;
|
||||
error = xfs_rmap_update(cur, <rec);
|
||||
if (error)
|
||||
goto out_error;
|
||||
} else if (ltrec.rm_startblock + ltrec.rm_blockcount == bno + len) {
|
||||
/*
|
||||
* overlap right hand side of extent: trim the length and update
|
||||
* the current record.
|
||||
*
|
||||
* ltbno ltlen
|
||||
* Orig: |oooooooooooooooooooo|
|
||||
* Freeing: |fffffffff|
|
||||
* Result: |rrrrrrrrrr|
|
||||
* bno len
|
||||
*/
|
||||
ltrec.rm_blockcount -= len;
|
||||
error = xfs_rmap_update(cur, <rec);
|
||||
if (error)
|
||||
goto out_error;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* overlap middle of extent: trim the length of the existing
|
||||
* record to the length of the new left-extent size, increment
|
||||
* the insertion position so we can insert a new record
|
||||
* containing the remaining right-extent space.
|
||||
*
|
||||
* ltbno ltlen
|
||||
* Orig: |oooooooooooooooooooo|
|
||||
* Freeing: |fffffffff|
|
||||
* Result: |rrrrr| |rrrr|
|
||||
* bno len
|
||||
*/
|
||||
xfs_extlen_t orig_len = ltrec.rm_blockcount;
|
||||
|
||||
ltrec.rm_blockcount = bno - ltrec.rm_startblock;
|
||||
error = xfs_rmap_update(cur, <rec);
|
||||
if (error)
|
||||
goto out_error;
|
||||
|
||||
error = xfs_btree_increment(cur, 0, &i);
|
||||
if (error)
|
||||
goto out_error;
|
||||
|
||||
cur->bc_rec.r.rm_startblock = bno + len;
|
||||
cur->bc_rec.r.rm_blockcount = orig_len - len -
|
||||
ltrec.rm_blockcount;
|
||||
cur->bc_rec.r.rm_owner = ltrec.rm_owner;
|
||||
if (ignore_off)
|
||||
cur->bc_rec.r.rm_offset = 0;
|
||||
else
|
||||
cur->bc_rec.r.rm_offset = offset + len;
|
||||
cur->bc_rec.r.rm_flags = flags;
|
||||
trace_xfs_rmap_insert(mp, cur->bc_private.a.agno,
|
||||
cur->bc_rec.r.rm_startblock,
|
||||
cur->bc_rec.r.rm_blockcount,
|
||||
cur->bc_rec.r.rm_owner,
|
||||
cur->bc_rec.r.rm_offset,
|
||||
cur->bc_rec.r.rm_flags);
|
||||
error = xfs_btree_insert(cur, &i);
|
||||
if (error)
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
out_done:
|
||||
trace_xfs_rmap_unmap_done(mp, cur->bc_private.a.agno, bno, len,
|
||||
unwritten, oinfo);
|
||||
out_error:
|
||||
if (error)
|
||||
trace_xfs_rmap_unmap_error(mp, cur->bc_private.a.agno,
|
||||
error, _RET_IP_);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a reference to an extent in the rmap btree.
|
||||
*/
|
||||
int
|
||||
xfs_rmap_free(
|
||||
struct xfs_trans *tp,
|
||||
@ -143,19 +349,23 @@ xfs_rmap_free(
|
||||
struct xfs_owner_info *oinfo)
|
||||
{
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
int error = 0;
|
||||
struct xfs_btree_cur *cur;
|
||||
int error;
|
||||
|
||||
if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
|
||||
return 0;
|
||||
|
||||
trace_xfs_rmap_unmap(mp, agno, bno, len, false, oinfo);
|
||||
if (1)
|
||||
cur = xfs_rmapbt_init_cursor(mp, tp, agbp, agno);
|
||||
|
||||
error = xfs_rmap_unmap(cur, bno, len, false, oinfo);
|
||||
if (error)
|
||||
goto out_error;
|
||||
trace_xfs_rmap_unmap_done(mp, agno, bno, len, false, oinfo);
|
||||
|
||||
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
|
||||
return 0;
|
||||
|
||||
out_error:
|
||||
trace_xfs_rmap_unmap_error(mp, agno, error, _RET_IP_);
|
||||
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user