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

Commit e890038e authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-fixes-for-linus-4.9-rc3' of...

Merge tag 'xfs-fixes-for-linus-4.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs

Pull xfs fixes from Dave Chinner:
 "This update contains fixes for most of the outstanding regressions
  introduced with the 4.9-rc1 XFS merge. There is also a fix for an
  iomap bug, too.

  This is a quite a bit larger than I'd prefer for a -rc3, but most of
  the change comes from cleaning up the new reflink copy on write code;
  it's much simpler and easier to understand now. These changes fixed
  several bugs in the new code, and it wasn't clear that there was an
  easier/simpler way to fix them. The rest of the fixes are the usual
  size you'd expect at this stage.

  I've left the commits to soak in linux-next for a some extra time
  because of the size before asking you to pull, no new problems with
  them have been reported so I think it's all OK.

  Summary:
   - iomap page offset masking fix for page faults
   - add IOMAP_REPORT to distinguish between read and fiemap map
     requests
   - cleanups to new shared data extent code
   - fix mount active status on failed log recovery
   - fix broken dquots in a buffer calculation
   - fix locking order issues and merge xfs_reflink_remap_range and
     xfs_file_share_range
   - rework unmapping of CoW extents and remove now unused functions
   - clean state when CoW is done"

* tag 'xfs-fixes-for-linus-4.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs: (25 commits)
  xfs: clear cowblocks tag when cow fork is emptied
  xfs: fix up inode cowblocks tracking tracepoints
  fs: Do to trim high file position bits in iomap_page_mkwrite_actor
  xfs: remove xfs_bunmapi_cow
  xfs: optimize xfs_reflink_end_cow
  xfs: optimize xfs_reflink_cancel_cow_blocks
  xfs: refactor xfs_bunmapi_cow
  xfs: optimize writes to reflink files
  xfs: don't bother looking at the refcount tree for reads
  xfs: handle "raw" delayed extents xfs_reflink_trim_around_shared
  xfs: add xfs_trim_extent
  iomap: add IOMAP_REPORT
  xfs: merge xfs_reflink_remap_range and xfs_file_share_range
  xfs: remove xfs_file_wait_for_io
  xfs: move inode locking from xfs_reflink_remap_range to xfs_file_share_range
  xfs: fix the same_inode check in xfs_file_share_range
  xfs: remove the same fs check from xfs_file_share_range
  libxfs: v3 inodes are only valid on crc-enabled filesystems
  libxfs: clean up _calc_dquots_per_chunk
  xfs: unset MS_ACTIVE if mount fails
  ...
parents 18c2152d c17a8ef4
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -433,8 +433,7 @@ iomap_page_mkwrite_actor(struct inode *inode, loff_t pos, loff_t length,
	struct page *page = data;
	int ret;

	ret = __block_write_begin_int(page, pos & ~PAGE_MASK, length,
			NULL, iomap);
	ret = __block_write_begin_int(page, pos, length, NULL, iomap);
	if (ret)
		return ret;

@@ -561,7 +560,7 @@ int iomap_fiemap(struct inode *inode, struct fiemap_extent_info *fi,
	}

	while (len > 0) {
		ret = iomap_apply(inode, start, len, 0, ops, &ctx,
		ret = iomap_apply(inode, start, len, IOMAP_REPORT, ops, &ctx,
				iomap_fiemap_actor);
		/* inode with no (attribute) mapping will give ENOENT */
		if (ret == -ENOENT)
+246 −172
Original line number Diff line number Diff line
@@ -3974,9 +3974,6 @@ xfs_bmap_remap_alloc(
	 * allocating, so skip that check by pretending to be freeing.
	 */
	error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
	if (error)
		goto error0;
error0:
	xfs_perag_put(args.pag);
	if (error)
		trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_);
@@ -3999,6 +3996,39 @@ xfs_bmap_alloc(
	return xfs_bmap_btalloc(ap);
}

/* Trim extent to fit a logical block range. */
void
xfs_trim_extent(
	struct xfs_bmbt_irec	*irec,
	xfs_fileoff_t		bno,
	xfs_filblks_t		len)
{
	xfs_fileoff_t		distance;
	xfs_fileoff_t		end = bno + len;

	if (irec->br_startoff + irec->br_blockcount <= bno ||
	    irec->br_startoff >= end) {
		irec->br_blockcount = 0;
		return;
	}

	if (irec->br_startoff < bno) {
		distance = bno - irec->br_startoff;
		if (isnullstartblock(irec->br_startblock))
			irec->br_startblock = DELAYSTARTBLOCK;
		if (irec->br_startblock != DELAYSTARTBLOCK &&
		    irec->br_startblock != HOLESTARTBLOCK)
			irec->br_startblock += distance;
		irec->br_startoff += distance;
		irec->br_blockcount -= distance;
	}

	if (end < irec->br_startoff + irec->br_blockcount) {
		distance = irec->br_startoff + irec->br_blockcount - end;
		irec->br_blockcount -= distance;
	}
}

/*
 * Trim the returned map to the required bounds
 */
@@ -4829,6 +4859,219 @@ xfs_bmap_split_indlen(
	return stolen;
}

int
xfs_bmap_del_extent_delay(
	struct xfs_inode	*ip,
	int			whichfork,
	xfs_extnum_t		*idx,
	struct xfs_bmbt_irec	*got,
	struct xfs_bmbt_irec	*del)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
	struct xfs_bmbt_irec	new;
	int64_t			da_old, da_new, da_diff = 0;
	xfs_fileoff_t		del_endoff, got_endoff;
	xfs_filblks_t		got_indlen, new_indlen, stolen;
	int			error = 0, state = 0;
	bool			isrt;

	XFS_STATS_INC(mp, xs_del_exlist);

	isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip);
	del_endoff = del->br_startoff + del->br_blockcount;
	got_endoff = got->br_startoff + got->br_blockcount;
	da_old = startblockval(got->br_startblock);
	da_new = 0;

	ASSERT(*idx >= 0);
	ASSERT(*idx < ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
	ASSERT(del->br_blockcount > 0);
	ASSERT(got->br_startoff <= del->br_startoff);
	ASSERT(got_endoff >= del_endoff);

	if (isrt) {
		int64_t rtexts = XFS_FSB_TO_B(mp, del->br_blockcount);

		do_div(rtexts, mp->m_sb.sb_rextsize);
		xfs_mod_frextents(mp, rtexts);
	}

	/*
	 * Update the inode delalloc counter now and wait to update the
	 * sb counters as we might have to borrow some blocks for the
	 * indirect block accounting.
	 */
	xfs_trans_reserve_quota_nblks(NULL, ip, -((long)del->br_blockcount), 0,
			isrt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
	ip->i_delayed_blks -= del->br_blockcount;

	if (whichfork == XFS_COW_FORK)
		state |= BMAP_COWFORK;

	if (got->br_startoff == del->br_startoff)
		state |= BMAP_LEFT_CONTIG;
	if (got_endoff == del_endoff)
		state |= BMAP_RIGHT_CONTIG;

	switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) {
	case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
		/*
		 * Matches the whole extent.  Delete the entry.
		 */
		xfs_iext_remove(ip, *idx, 1, state);
		--*idx;
		break;
	case BMAP_LEFT_CONTIG:
		/*
		 * Deleting the first part of the extent.
		 */
		trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
		got->br_startoff = del_endoff;
		got->br_blockcount -= del->br_blockcount;
		da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip,
				got->br_blockcount), da_old);
		got->br_startblock = nullstartblock((int)da_new);
		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
		break;
	case BMAP_RIGHT_CONTIG:
		/*
		 * Deleting the last part of the extent.
		 */
		trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
		got->br_blockcount = got->br_blockcount - del->br_blockcount;
		da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip,
				got->br_blockcount), da_old);
		got->br_startblock = nullstartblock((int)da_new);
		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
		break;
	case 0:
		/*
		 * Deleting the middle of the extent.
		 *
		 * Distribute the original indlen reservation across the two new
		 * extents.  Steal blocks from the deleted extent if necessary.
		 * Stealing blocks simply fudges the fdblocks accounting below.
		 * Warn if either of the new indlen reservations is zero as this
		 * can lead to delalloc problems.
		 */
		trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);

		got->br_blockcount = del->br_startoff - got->br_startoff;
		got_indlen = xfs_bmap_worst_indlen(ip, got->br_blockcount);

		new.br_blockcount = got_endoff - del_endoff;
		new_indlen = xfs_bmap_worst_indlen(ip, new.br_blockcount);

		WARN_ON_ONCE(!got_indlen || !new_indlen);
		stolen = xfs_bmap_split_indlen(da_old, &got_indlen, &new_indlen,
						       del->br_blockcount);

		got->br_startblock = nullstartblock((int)got_indlen);
		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
		trace_xfs_bmap_post_update(ip, *idx, 0, _THIS_IP_);

		new.br_startoff = del_endoff;
		new.br_state = got->br_state;
		new.br_startblock = nullstartblock((int)new_indlen);

		++*idx;
		xfs_iext_insert(ip, *idx, 1, &new, state);

		da_new = got_indlen + new_indlen - stolen;
		del->br_blockcount -= stolen;
		break;
	}

	ASSERT(da_old >= da_new);
	da_diff = da_old - da_new;
	if (!isrt)
		da_diff += del->br_blockcount;
	if (da_diff)
		xfs_mod_fdblocks(mp, da_diff, false);
	return error;
}

void
xfs_bmap_del_extent_cow(
	struct xfs_inode	*ip,
	xfs_extnum_t		*idx,
	struct xfs_bmbt_irec	*got,
	struct xfs_bmbt_irec	*del)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
	struct xfs_bmbt_irec	new;
	xfs_fileoff_t		del_endoff, got_endoff;
	int			state = BMAP_COWFORK;

	XFS_STATS_INC(mp, xs_del_exlist);

	del_endoff = del->br_startoff + del->br_blockcount;
	got_endoff = got->br_startoff + got->br_blockcount;

	ASSERT(*idx >= 0);
	ASSERT(*idx < ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
	ASSERT(del->br_blockcount > 0);
	ASSERT(got->br_startoff <= del->br_startoff);
	ASSERT(got_endoff >= del_endoff);
	ASSERT(!isnullstartblock(got->br_startblock));

	if (got->br_startoff == del->br_startoff)
		state |= BMAP_LEFT_CONTIG;
	if (got_endoff == del_endoff)
		state |= BMAP_RIGHT_CONTIG;

	switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) {
	case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
		/*
		 * Matches the whole extent.  Delete the entry.
		 */
		xfs_iext_remove(ip, *idx, 1, state);
		--*idx;
		break;
	case BMAP_LEFT_CONTIG:
		/*
		 * Deleting the first part of the extent.
		 */
		trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
		got->br_startoff = del_endoff;
		got->br_blockcount -= del->br_blockcount;
		got->br_startblock = del->br_startblock + del->br_blockcount;
		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
		break;
	case BMAP_RIGHT_CONTIG:
		/*
		 * Deleting the last part of the extent.
		 */
		trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
		got->br_blockcount -= del->br_blockcount;
		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
		break;
	case 0:
		/*
		 * Deleting the middle of the extent.
		 */
		trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
		got->br_blockcount = del->br_startoff - got->br_startoff;
		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
		trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);

		new.br_startoff = del_endoff;
		new.br_blockcount = got_endoff - del_endoff;
		new.br_state = got->br_state;
		new.br_startblock = del->br_startblock + del->br_blockcount;

		++*idx;
		xfs_iext_insert(ip, *idx, 1, &new, state);
		break;
	}
}

/*
 * Called by xfs_bmapi to update file extent records and the btree
 * after removing space (or undoing a delayed allocation).
@@ -5171,175 +5414,6 @@ 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
+7 −1
Original line number Diff line number Diff line
@@ -190,6 +190,8 @@ void xfs_bmap_trace_exlist(struct xfs_inode *ip, xfs_extnum_t cnt,
#define	XFS_BMAP_TRACE_EXLIST(ip,c,w)
#endif

void	xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno,
		xfs_filblks_t len);
int	xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
void	xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork);
void	xfs_bmap_add_free(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
@@ -221,7 +223,11 @@ 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_bmap_del_extent_delay(struct xfs_inode *ip, int whichfork,
		xfs_extnum_t *idx, struct xfs_bmbt_irec *got,
		struct xfs_bmbt_irec *del);
void	xfs_bmap_del_extent_cow(struct xfs_inode *ip, xfs_extnum_t *idx,
		struct xfs_bmbt_irec *got, 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);
+1 −1
Original line number Diff line number Diff line
@@ -4826,7 +4826,7 @@ xfs_btree_calc_size(
	return rval;
}

int
static int
xfs_btree_count_blocks_helper(
	struct xfs_btree_cur	*cur,
	int			level,
+1 −2
Original line number Diff line number Diff line
@@ -191,8 +191,7 @@ xfs_dquot_buf_verify_crc(
	if (mp->m_quotainfo)
		ndquots = mp->m_quotainfo->qi_dqperchunk;
	else
		ndquots = xfs_calc_dquots_per_chunk(
					XFS_BB_TO_FSB(mp, bp->b_length));
		ndquots = xfs_calc_dquots_per_chunk(bp->b_length);

	for (i = 0; i < ndquots; i++, d++) {
		if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk),
Loading