diff options
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
| -rw-r--r-- | fs/xfs/xfs_reflink.c | 101 | 
1 files changed, 59 insertions, 42 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index ffe6fe7a7eb5..ab2270a87196 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -155,6 +155,7 @@  int  xfs_reflink_find_shared(  	struct xfs_mount	*mp, +	struct xfs_trans	*tp,  	xfs_agnumber_t		agno,  	xfs_agblock_t		agbno,  	xfs_extlen_t		aglen, @@ -166,18 +167,18 @@ xfs_reflink_find_shared(  	struct xfs_btree_cur	*cur;  	int			error; -	error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp); +	error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp);  	if (error)  		return error; -	cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL); +	cur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, NULL);  	error = xfs_refcount_find_shared(cur, agbno, aglen, fbno, flen,  			find_end_of_shared);  	xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); -	xfs_buf_relse(agbp); +	xfs_trans_brelse(tp, agbp);  	return error;  } @@ -217,7 +218,7 @@ xfs_reflink_trim_around_shared(  	agbno = XFS_FSB_TO_AGBNO(ip->i_mount, irec->br_startblock);  	aglen = irec->br_blockcount; -	error = xfs_reflink_find_shared(ip->i_mount, agno, agbno, +	error = xfs_reflink_find_shared(ip->i_mount, NULL, agno, agbno,  			aglen, &fbno, &flen, true);  	if (error)  		return error; @@ -1373,8 +1374,8 @@ xfs_reflink_dirty_extents(  			agbno = XFS_FSB_TO_AGBNO(mp, map[1].br_startblock);  			aglen = map[1].br_blockcount; -			error = xfs_reflink_find_shared(mp, agno, agbno, aglen, -					&rbno, &rlen, true); +			error = xfs_reflink_find_shared(mp, NULL, agno, agbno, +					aglen, &rbno, &rlen, true);  			if (error)  				goto out;  			if (rbno == NULLAGBLOCK) @@ -1405,57 +1406,73 @@ out:  	return error;  } -/* Clear the inode reflink flag if there are no shared extents. */ +/* Does this inode need the reflink flag? */  int -xfs_reflink_clear_inode_flag( -	struct xfs_inode	*ip, -	struct xfs_trans	**tpp) +xfs_reflink_inode_has_shared_extents( +	struct xfs_trans		*tp, +	struct xfs_inode		*ip, +	bool				*has_shared)  { -	struct xfs_mount	*mp = ip->i_mount; -	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; - -	ASSERT(xfs_is_reflink_inode(ip)); +	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; -	fbno = 0; -	end = XFS_B_TO_FSB(mp, i_size_read(VFS_I(ip))); -	while (end - fbno > 0) { -		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); +	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; -		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; +	*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, agno, agbno, aglen, +		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) +		if (rbno != NULLAGBLOCK) { +			*has_shared = true;  			return 0; +		}  next: -		fbno = map.br_startoff + map.br_blockcount; +		found = xfs_iext_get_extent(ifp, ++idx, &got);  	} +	return 0; +} + +/* Clear the inode reflink flag if there are no shared extents. */ +int +xfs_reflink_clear_inode_flag( +	struct xfs_inode	*ip, +	struct xfs_trans	**tpp) +{ +	bool			needs_flag; +	int			error = 0; + +	ASSERT(xfs_is_reflink_inode(ip)); + +	error = xfs_reflink_inode_has_shared_extents(*tpp, ip, &needs_flag); +	if (error || needs_flag) +		return error; +  	/*  	 * We didn't find any shared blocks so turn off the reflink flag.  	 * First, get rid of any leftover CoW mappings.  | 
