Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4862cfe8 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

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: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent ef473667
Loading
Loading
Loading
Loading
+170 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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);