xfs: separate function to check if inode shares extents
Separate the "clear reflink flag" function into one function that checks if the flag is needed, and a second function that checks and clears the flag. The inode scrub code will want to check the necessity of the flag without clearing it. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Brian Foster <bfoster@redhat.com>
This commit is contained in:
parent
92ff7285f1
commit
ea7cdd7b7b
@ -1406,56 +1406,72 @@ out:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Does this inode need the reflink flag? */
|
||||||
|
int
|
||||||
|
xfs_reflink_inode_has_shared_extents(
|
||||||
|
struct xfs_trans *tp,
|
||||||
|
struct xfs_inode *ip,
|
||||||
|
bool *has_shared)
|
||||||
|
{
|
||||||
|
struct xfs_bmbt_irec got;
|
||||||
|
struct xfs_mount *mp = ip->i_mount;
|
||||||
|
struct xfs_ifork *ifp;
|
||||||
|
xfs_agnumber_t agno;
|
||||||
|
xfs_agblock_t agbno;
|
||||||
|
xfs_extlen_t aglen;
|
||||||
|
xfs_agblock_t rbno;
|
||||||
|
xfs_extlen_t rlen;
|
||||||
|
xfs_extnum_t idx;
|
||||||
|
bool found;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
|
||||||
|
if (!(ifp->if_flags & XFS_IFEXTENTS)) {
|
||||||
|
error = xfs_iread_extents(tp, ip, XFS_DATA_FORK);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
*has_shared = false;
|
||||||
|
found = xfs_iext_lookup_extent(ip, ifp, 0, &idx, &got);
|
||||||
|
while (found) {
|
||||||
|
if (isnullstartblock(got.br_startblock) ||
|
||||||
|
got.br_state != XFS_EXT_NORM)
|
||||||
|
goto next;
|
||||||
|
agno = XFS_FSB_TO_AGNO(mp, got.br_startblock);
|
||||||
|
agbno = XFS_FSB_TO_AGBNO(mp, got.br_startblock);
|
||||||
|
aglen = got.br_blockcount;
|
||||||
|
|
||||||
|
error = xfs_reflink_find_shared(mp, tp, agno, agbno, aglen,
|
||||||
|
&rbno, &rlen, false);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
/* Is there still a shared block here? */
|
||||||
|
if (rbno != NULLAGBLOCK) {
|
||||||
|
*has_shared = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
next:
|
||||||
|
found = xfs_iext_get_extent(ifp, ++idx, &got);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Clear the inode reflink flag if there are no shared extents. */
|
/* Clear the inode reflink flag if there are no shared extents. */
|
||||||
int
|
int
|
||||||
xfs_reflink_clear_inode_flag(
|
xfs_reflink_clear_inode_flag(
|
||||||
struct xfs_inode *ip,
|
struct xfs_inode *ip,
|
||||||
struct xfs_trans **tpp)
|
struct xfs_trans **tpp)
|
||||||
{
|
{
|
||||||
struct xfs_mount *mp = ip->i_mount;
|
bool needs_flag;
|
||||||
xfs_fileoff_t fbno;
|
|
||||||
xfs_filblks_t end;
|
|
||||||
xfs_agnumber_t agno;
|
|
||||||
xfs_agblock_t agbno;
|
|
||||||
xfs_extlen_t aglen;
|
|
||||||
xfs_agblock_t rbno;
|
|
||||||
xfs_extlen_t rlen;
|
|
||||||
struct xfs_bmbt_irec map;
|
|
||||||
int nmaps;
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
ASSERT(xfs_is_reflink_inode(ip));
|
ASSERT(xfs_is_reflink_inode(ip));
|
||||||
|
|
||||||
fbno = 0;
|
error = xfs_reflink_inode_has_shared_extents(*tpp, ip, &needs_flag);
|
||||||
end = XFS_B_TO_FSB(mp, i_size_read(VFS_I(ip)));
|
if (error || needs_flag)
|
||||||
while (end - fbno > 0) {
|
return error;
|
||||||
nmaps = 1;
|
|
||||||
/*
|
|
||||||
* Look for extents in the file. Skip holes, delalloc, or
|
|
||||||
* unwritten extents; they can't be reflinked.
|
|
||||||
*/
|
|
||||||
error = xfs_bmapi_read(ip, fbno, end - fbno, &map, &nmaps, 0);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
if (nmaps == 0)
|
|
||||||
break;
|
|
||||||
if (!xfs_bmap_is_real_extent(&map))
|
|
||||||
goto next;
|
|
||||||
|
|
||||||
agno = XFS_FSB_TO_AGNO(mp, map.br_startblock);
|
|
||||||
agbno = XFS_FSB_TO_AGBNO(mp, map.br_startblock);
|
|
||||||
aglen = map.br_blockcount;
|
|
||||||
|
|
||||||
error = xfs_reflink_find_shared(mp, *tpp, agno, agbno, aglen,
|
|
||||||
&rbno, &rlen, false);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
/* Is there still a shared block here? */
|
|
||||||
if (rbno != NULLAGBLOCK)
|
|
||||||
return 0;
|
|
||||||
next:
|
|
||||||
fbno = map.br_startoff + map.br_blockcount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We didn't find any shared blocks so turn off the reflink flag.
|
* We didn't find any shared blocks so turn off the reflink flag.
|
||||||
|
@ -47,6 +47,8 @@ extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset,
|
|||||||
extern int xfs_reflink_recover_cow(struct xfs_mount *mp);
|
extern int xfs_reflink_recover_cow(struct xfs_mount *mp);
|
||||||
extern int xfs_reflink_remap_range(struct file *file_in, loff_t pos_in,
|
extern int xfs_reflink_remap_range(struct file *file_in, loff_t pos_in,
|
||||||
struct file *file_out, loff_t pos_out, u64 len, bool is_dedupe);
|
struct file *file_out, loff_t pos_out, u64 len, bool is_dedupe);
|
||||||
|
extern int xfs_reflink_inode_has_shared_extents(struct xfs_trans *tp,
|
||||||
|
struct xfs_inode *ip, bool *has_shared);
|
||||||
extern int xfs_reflink_clear_inode_flag(struct xfs_inode *ip,
|
extern int xfs_reflink_clear_inode_flag(struct xfs_inode *ip,
|
||||||
struct xfs_trans **tpp);
|
struct xfs_trans **tpp);
|
||||||
extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset,
|
extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset,
|
||||||
|
Loading…
Reference in New Issue
Block a user