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

Commit 5ff0b9e1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-for-linus-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs

Pull xfs update from Dave Chinner:
 "This update contains:
   - various cleanups
   - log recovery debug hooks
   - seek hole/data implementation merge
   - extent shift rework to fix collapse range bugs
   - various sparse warning fixes
   - log recovery transaction processing rework to fix use after free
     bugs
   - metadata buffer IO infrastructuer rework to ensure all buffers
     under IO have valid reference counts
   - various fixes for ondisk flags, writeback and zero range corner
     cases"

* tag 'xfs-for-linus-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs: (56 commits)
  xfs: fix agno increment in xfs_inumbers() loop
  xfs: xfs_iflush_done checks the wrong log item callback
  xfs: flush the range before zero range conversion
  xfs: restore buffer_head unwritten bit on ioend cancel
  xfs: check for null dquot in xfs_quota_calc_throttle()
  xfs: fix crc field handling in xfs_sb_to/from_disk
  xfs: don't send null bp to xfs_trans_brelse()
  xfs: check for inode size overflow in xfs_new_eof()
  xfs: only set extent size hint when asked
  xfs: project id inheritance is a directory only flag
  xfs: kill time.h
  xfs: compat_xfs_bstat does not have forkoff
  xfs: simplify xfs_zero_remaining_bytes
  xfs: check xfs_buf_read_uncached returns correctly
  xfs: introduce xfs_buf_submit[_wait]
  xfs: kill xfs_bioerror_relse
  xfs: xfs_bioerror can die.
  xfs: kill xfs_bdstrat_cb
  xfs: rework xfs_buf_bio_endio error handling
  xfs: xfs_buf_ioend and xfs_buf_iodone_work duplicate functionality
  ...
parents 77c688ac 6889e783
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@
#include <linux/swap.h>
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
#include "time.h"
#include "kmem.h"
#include "xfs_message.h"

+4 −0
Original line number Diff line number Diff line
@@ -2209,6 +2209,10 @@ xfs_agf_verify(
	      be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)))
		return false;

	if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) > XFS_BTREE_MAXLEVELS)
		return false;

	/*
	 * during growfs operations, the perag is not fully initialised,
	 * so we can't use it for any useful checking. growfs ensures we can't
+241 −124
Original line number Diff line number Diff line
@@ -5403,23 +5403,224 @@ error0:
	return error;
}

/*
 * Determine whether an extent shift can be accomplished by a merge with the
 * extent that precedes the target hole of the shift.
 */
STATIC bool
xfs_bmse_can_merge(
	struct xfs_bmbt_irec	*left,	/* preceding extent */
	struct xfs_bmbt_irec	*got,	/* current extent to shift */
	xfs_fileoff_t		shift)	/* shift fsb */
{
	xfs_fileoff_t		startoff;

	startoff = got->br_startoff - shift;

	/*
	 * The extent, once shifted, must be adjacent in-file and on-disk with
	 * the preceding extent.
	 */
	if ((left->br_startoff + left->br_blockcount != startoff) ||
	    (left->br_startblock + left->br_blockcount != got->br_startblock) ||
	    (left->br_state != got->br_state) ||
	    (left->br_blockcount + got->br_blockcount > MAXEXTLEN))
		return false;

	return true;
}

/*
 * A bmap extent shift adjusts the file offset of an extent to fill a preceding
 * hole in the file. If an extent shift would result in the extent being fully
 * adjacent to the extent that currently precedes the hole, we can merge with
 * the preceding extent rather than do the shift.
 *
 * This function assumes the caller has verified a shift-by-merge is possible
 * with the provided extents via xfs_bmse_can_merge().
 */
STATIC int
xfs_bmse_merge(
	struct xfs_inode		*ip,
	int				whichfork,
	xfs_fileoff_t			shift,		/* shift fsb */
	int				current_ext,	/* idx of gotp */
	struct xfs_bmbt_rec_host	*gotp,		/* extent to shift */
	struct xfs_bmbt_rec_host	*leftp,		/* preceding extent */
	struct xfs_btree_cur		*cur,
	int				*logflags)	/* output */
{
	struct xfs_ifork		*ifp;
	struct xfs_bmbt_irec		got;
	struct xfs_bmbt_irec		left;
	xfs_filblks_t			blockcount;
	int				error, i;

	ifp = XFS_IFORK_PTR(ip, whichfork);
	xfs_bmbt_get_all(gotp, &got);
	xfs_bmbt_get_all(leftp, &left);
	blockcount = left.br_blockcount + got.br_blockcount;

	ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
	ASSERT(xfs_bmse_can_merge(&left, &got, shift));

	/*
	 * Merge the in-core extents. Note that the host record pointers and
	 * current_ext index are invalid once the extent has been removed via
	 * xfs_iext_remove().
	 */
	xfs_bmbt_set_blockcount(leftp, blockcount);
	xfs_iext_remove(ip, current_ext, 1, 0);

	/*
	 * Update the on-disk extent count, the btree if necessary and log the
	 * inode.
	 */
	XFS_IFORK_NEXT_SET(ip, whichfork,
			   XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
	*logflags |= XFS_ILOG_CORE;
	if (!cur) {
		*logflags |= XFS_ILOG_DEXT;
		return 0;
	}

	/* lookup and remove the extent to merge */
	error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock,
				   got.br_blockcount, &i);
	if (error)
		goto out_error;
	XFS_WANT_CORRUPTED_GOTO(i == 1, out_error);

	error = xfs_btree_delete(cur, &i);
	if (error)
		goto out_error;
	XFS_WANT_CORRUPTED_GOTO(i == 1, out_error);

	/* lookup and update size of the previous extent */
	error = xfs_bmbt_lookup_eq(cur, left.br_startoff, left.br_startblock,
				   left.br_blockcount, &i);
	if (error)
		goto out_error;
	XFS_WANT_CORRUPTED_GOTO(i == 1, out_error);

	left.br_blockcount = blockcount;

	error = xfs_bmbt_update(cur, left.br_startoff, left.br_startblock,
				left.br_blockcount, left.br_state);
	if (error)
		goto out_error;

	return 0;

out_error:
	return error;
}

/*
 * Shift a single extent.
 */
STATIC int
xfs_bmse_shift_one(
	struct xfs_inode		*ip,
	int				whichfork,
	xfs_fileoff_t			offset_shift_fsb,
	int				*current_ext,
	struct xfs_bmbt_rec_host	*gotp,
	struct xfs_btree_cur		*cur,
	int				*logflags)
{
	struct xfs_ifork		*ifp;
	xfs_fileoff_t			startoff;
	struct xfs_bmbt_rec_host	*leftp;
	struct xfs_bmbt_irec		got;
	struct xfs_bmbt_irec		left;
	int				error;
	int				i;

	ifp = XFS_IFORK_PTR(ip, whichfork);

	xfs_bmbt_get_all(gotp, &got);
	startoff = got.br_startoff - offset_shift_fsb;

	/* delalloc extents should be prevented by caller */
	XFS_WANT_CORRUPTED_GOTO(!isnullstartblock(got.br_startblock),
				out_error);

	/*
	 * If this is the first extent in the file, make sure there's enough
	 * room at the start of the file and jump right to the shift as there's
	 * no left extent to merge.
	 */
	if (*current_ext == 0) {
		if (got.br_startoff < offset_shift_fsb)
			return -EINVAL;
		goto shift_extent;
	}

	/* grab the left extent and check for a large enough hole */
	leftp = xfs_iext_get_ext(ifp, *current_ext - 1);
	xfs_bmbt_get_all(leftp, &left);

	if (startoff < left.br_startoff + left.br_blockcount)
		return -EINVAL;

	/* check whether to merge the extent or shift it down */
	if (!xfs_bmse_can_merge(&left, &got, offset_shift_fsb))
		goto shift_extent;

	return xfs_bmse_merge(ip, whichfork, offset_shift_fsb, *current_ext,
			      gotp, leftp, cur, logflags);

shift_extent:
	/*
	 * Increment the extent index for the next iteration, update the start
	 * offset of the in-core extent and update the btree if applicable.
	 */
	(*current_ext)++;
	xfs_bmbt_set_startoff(gotp, startoff);
	*logflags |= XFS_ILOG_CORE;
	if (!cur) {
		*logflags |= XFS_ILOG_DEXT;
		return 0;
	}

	error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock,
				   got.br_blockcount, &i);
	if (error)
		return error;
	XFS_WANT_CORRUPTED_GOTO(i == 1, out_error);

	got.br_startoff = startoff;
	error = xfs_bmbt_update(cur, got.br_startoff, got.br_startblock,
				got.br_blockcount, got.br_state);
	if (error)
		return error;

	return 0;

out_error:
	return error;
}

/*
 * Shift extent records to the left to cover a hole.
 *
 * The maximum number of extents to be shifted in a single operation
 * is @num_exts, and @current_ext keeps track of the current extent
 * index we have shifted. @offset_shift_fsb is the length by which each
 * extent is shifted. If there is no hole to shift the extents
 * into, this will be considered invalid operation and we abort immediately.
 * The maximum number of extents to be shifted in a single operation is
 * @num_exts. @start_fsb specifies the file offset to start the shift and the
 * file offset where we've left off is returned in @next_fsb. @offset_shift_fsb
 * is the length by which each extent is shifted. If there is no hole to shift
 * the extents into, this will be considered invalid operation and we abort
 * immediately.
 */
int
xfs_bmap_shift_extents(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	int			*done,
	xfs_fileoff_t		start_fsb,
	xfs_fileoff_t		offset_shift_fsb,
	xfs_extnum_t		*current_ext,
	int			*done,
	xfs_fileoff_t		*next_fsb,
	xfs_fsblock_t		*firstblock,
	struct xfs_bmap_free	*flist,
	int			num_exts)
@@ -5427,16 +5628,13 @@ xfs_bmap_shift_extents(
	struct xfs_btree_cur		*cur = NULL;
	struct xfs_bmbt_rec_host	*gotp;
	struct xfs_bmbt_irec            got;
	struct xfs_bmbt_irec		left;
	struct xfs_mount		*mp = ip->i_mount;
	struct xfs_ifork		*ifp;
	xfs_extnum_t			nexts = 0;
	xfs_fileoff_t			startoff;
	xfs_extnum_t			current_ext;
	int				error = 0;
	int				i;
	int				whichfork = XFS_DATA_FORK;
	int				logflags = 0;
	xfs_filblks_t			blockcount = 0;
	int				total_extents;

	if (unlikely(XFS_TEST_ERROR(
@@ -5451,7 +5649,8 @@ xfs_bmap_shift_extents(
	if (XFS_FORCED_SHUTDOWN(mp))
		return -EIO;

	ASSERT(current_ext != NULL);
	ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));

	ifp = XFS_IFORK_PTR(ip, whichfork);
	if (!(ifp->if_flags & XFS_IFEXTENTS)) {
@@ -5461,23 +5660,6 @@ xfs_bmap_shift_extents(
			return error;
	}

	/*
	 * If *current_ext is 0, we would need to lookup the extent
	 * from where we would start shifting and store it in gotp.
	 */
	if (!*current_ext) {
		gotp = xfs_iext_bno_to_ext(ifp, start_fsb, current_ext);
		/*
		 * gotp can be null in 2 cases: 1) if there are no extents
		 * or 2) start_fsb lies in a hole beyond which there are
		 * no extents. Either way, we are done.
		 */
		if (!gotp) {
			*done = 1;
			return 0;
		}
	}

	if (ifp->if_flags & XFS_IFBROOT) {
		cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
		cur->bc_private.b.firstblock = *firstblock;
@@ -5486,112 +5668,46 @@ xfs_bmap_shift_extents(
	}

	/*
	 * There may be delalloc extents in the data fork before the range we
	 * are collapsing out, so we cannot
	 * use the count of real extents here. Instead we have to calculate it
	 * from the incore fork.
	 */
	total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
	while (nexts++ < num_exts && *current_ext < total_extents) {

		gotp = xfs_iext_get_ext(ifp, *current_ext);
		xfs_bmbt_get_all(gotp, &got);
		startoff = got.br_startoff - offset_shift_fsb;

		/*
		 * Before shifting extent into hole, make sure that the hole
		 * is large enough to accomodate the shift.
		 */
		if (*current_ext) {
			xfs_bmbt_get_all(xfs_iext_get_ext(ifp,
						*current_ext - 1), &left);

			if (startoff < left.br_startoff + left.br_blockcount)
				error = -EINVAL;
		} else if (offset_shift_fsb > got.br_startoff) {
			/*
			 * When first extent is shifted, offset_shift_fsb
			 * should be less than the stating offset of
			 * the first extent.
	 * Look up the extent index for the fsb where we start shifting. We can
	 * henceforth iterate with current_ext as extent list changes are locked
	 * out via ilock.
	 *
	 * gotp can be null in 2 cases: 1) if there are no extents or 2)
	 * start_fsb lies in a hole beyond which there are no extents. Either
	 * way, we are done.
	 */
			error = -EINVAL;
		}

		if (error)
			goto del_cursor;

		if (cur) {
			error = xfs_bmbt_lookup_eq(cur, got.br_startoff,
						   got.br_startblock,
						   got.br_blockcount,
						   &i);
			if (error)
				goto del_cursor;
			XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor);
		}

		/* Check if we can merge 2 adjacent extents */
		if (*current_ext &&
		    left.br_startoff + left.br_blockcount == startoff &&
		    left.br_startblock + left.br_blockcount ==
				got.br_startblock &&
		    left.br_state == got.br_state &&
		    left.br_blockcount + got.br_blockcount <= MAXEXTLEN) {
			blockcount = left.br_blockcount +
				got.br_blockcount;
			xfs_iext_remove(ip, *current_ext, 1, 0);
			logflags |= XFS_ILOG_CORE;
			if (cur) {
				error = xfs_btree_delete(cur, &i);
				if (error)
					goto del_cursor;
				XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor);
			} else {
				logflags |= XFS_ILOG_DEXT;
			}
			XFS_IFORK_NEXT_SET(ip, whichfork,
				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
			gotp = xfs_iext_get_ext(ifp, --*current_ext);
			xfs_bmbt_get_all(gotp, &got);

			/* Make cursor point to the extent we will update */
			if (cur) {
				error = xfs_bmbt_lookup_eq(cur, got.br_startoff,
							   got.br_startblock,
							   got.br_blockcount,
							   &i);
				if (error)
	gotp = xfs_iext_bno_to_ext(ifp, start_fsb, &current_ext);
	if (!gotp) {
		*done = 1;
		goto del_cursor;
				XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor);
			}

			xfs_bmbt_set_blockcount(gotp, blockcount);
			got.br_blockcount = blockcount;
		} else {
			/* We have to update the startoff */
			xfs_bmbt_set_startoff(gotp, startoff);
			got.br_startoff = startoff;
	}

		logflags |= XFS_ILOG_CORE;
		if (cur) {
			error = xfs_bmbt_update(cur, got.br_startoff,
						got.br_startblock,
						got.br_blockcount,
						got.br_state);
	/*
	 * There may be delalloc extents in the data fork before the range we
	 * are collapsing out, so we cannot use the count of real extents here.
	 * Instead we have to calculate it from the incore fork.
	 */
	total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
	while (nexts++ < num_exts && current_ext < total_extents) {
		error = xfs_bmse_shift_one(ip, whichfork, offset_shift_fsb,
					&current_ext, gotp, cur, &logflags);
		if (error)
			goto del_cursor;
		} else {
			logflags |= XFS_ILOG_DEXT;
		}

		(*current_ext)++;
		/* update total extent count and grab the next record */
		total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
		if (current_ext >= total_extents)
			break;
		gotp = xfs_iext_get_ext(ifp, current_ext);
	}

	/* Check if we are done */
	if (*current_ext == total_extents)
	if (current_ext == total_extents) {
		*done = 1;
	} else if (next_fsb) {
		xfs_bmbt_get_all(gotp, &got);
		*next_fsb = got.br_startoff;
	}

del_cursor:
	if (cur)
@@ -5600,5 +5716,6 @@ del_cursor:

	if (logflags)
		xfs_trans_log_inode(tp, ip, logflags);

	return error;
}
+3 −4
Original line number Diff line number Diff line
@@ -178,9 +178,8 @@ 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);
int	xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip,
		int *done, xfs_fileoff_t start_fsb,
		xfs_fileoff_t offset_shift_fsb, xfs_extnum_t *current_ext,
		xfs_fsblock_t *firstblock, struct xfs_bmap_free	*flist,
		int num_exts);
		xfs_fileoff_t start_fsb, xfs_fileoff_t offset_shift_fsb,
		int *done, xfs_fileoff_t *next_fsb, xfs_fsblock_t *firstblock,
		struct xfs_bmap_free *flist, int num_exts);

#endif	/* __XFS_BMAP_H__ */
+2 −1
Original line number Diff line number Diff line
@@ -2563,6 +2563,7 @@ xfs_da_get_buf(
				    mapp, nmap, 0);
	error = bp ? bp->b_error : -EIO;
	if (error) {
		if (bp)
			xfs_trans_brelse(trans, bp);
		goto out_free;
	}
Loading