diff options
Diffstat (limited to 'fs/xfs')
40 files changed, 502 insertions, 240 deletions
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 0da80019a917..83ed7715f856 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -702,7 +702,7 @@ xfs_alloc_ag_vextent(  	ASSERT(args->agbno % args->alignment == 0);  	/* if not file data, insert new block into the reverse map btree */ -	if (args->oinfo.oi_owner != XFS_RMAP_OWN_UNKNOWN) { +	if (!xfs_rmap_should_skip_owner_update(&args->oinfo)) {  		error = xfs_rmap_alloc(args->tp, args->agbp, args->agno,  				       args->agbno, args->len, &args->oinfo);  		if (error) @@ -1682,7 +1682,7 @@ xfs_free_ag_extent(  	bno_cur = cnt_cur = NULL;  	mp = tp->t_mountp; -	if (oinfo->oi_owner != XFS_RMAP_OWN_UNKNOWN) { +	if (!xfs_rmap_should_skip_owner_update(oinfo)) {  		error = xfs_rmap_free(tp, agbp, agno, bno, len, oinfo);  		if (error)  			goto error0; diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 6249c92671de..a76914db72ef 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -212,6 +212,7 @@ xfs_attr_set(  	int			flags)  {  	struct xfs_mount	*mp = dp->i_mount; +	struct xfs_buf		*leaf_bp = NULL;  	struct xfs_da_args	args;  	struct xfs_defer_ops	dfops;  	struct xfs_trans_res	tres; @@ -327,9 +328,16 @@ xfs_attr_set(  		 * GROT: another possible req'mt for a double-split btree op.  		 */  		xfs_defer_init(args.dfops, args.firstblock); -		error = xfs_attr_shortform_to_leaf(&args); +		error = xfs_attr_shortform_to_leaf(&args, &leaf_bp);  		if (error)  			goto out_defer_cancel; +		/* +		 * Prevent the leaf buffer from being unlocked so that a +		 * concurrent AIL push cannot grab the half-baked leaf +		 * buffer and run into problems with the write verifier. +		 */ +		xfs_trans_bhold(args.trans, leaf_bp); +		xfs_defer_bjoin(args.dfops, leaf_bp);  		xfs_defer_ijoin(args.dfops, dp);  		error = xfs_defer_finish(&args.trans, args.dfops);  		if (error) @@ -337,13 +345,14 @@ xfs_attr_set(  		/*  		 * Commit the leaf transformation.  We'll need another (linked) -		 * transaction to add the new attribute to the leaf. +		 * transaction to add the new attribute to the leaf, which +		 * means that we have to hold & join the leaf buffer here too.  		 */ -  		error = xfs_trans_roll_inode(&args.trans, dp);  		if (error)  			goto out; - +		xfs_trans_bjoin(args.trans, leaf_bp); +		leaf_bp = NULL;  	}  	if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) @@ -374,8 +383,9 @@ xfs_attr_set(  out_defer_cancel:  	xfs_defer_cancel(&dfops); -	args.trans = NULL;  out: +	if (leaf_bp) +		xfs_trans_brelse(args.trans, leaf_bp);  	if (args.trans)  		xfs_trans_cancel(args.trans);  	xfs_iunlock(dp, XFS_ILOCK_EXCL); diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c index 53cc8b986eac..601eaa36f1ad 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.c +++ b/fs/xfs/libxfs/xfs_attr_leaf.c @@ -735,10 +735,13 @@ xfs_attr_shortform_getvalue(xfs_da_args_t *args)  }  /* - * Convert from using the shortform to the leaf. + * Convert from using the shortform to the leaf.  On success, return the + * buffer so that we can keep it locked until we're totally done with it.   */  int -xfs_attr_shortform_to_leaf(xfs_da_args_t *args) +xfs_attr_shortform_to_leaf( +	struct xfs_da_args	*args, +	struct xfs_buf		**leaf_bp)  {  	xfs_inode_t *dp;  	xfs_attr_shortform_t *sf; @@ -818,7 +821,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)  		sfe = XFS_ATTR_SF_NEXTENTRY(sfe);  	}  	error = 0; - +	*leaf_bp = bp;  out:  	kmem_free(tmpbuffer);  	return error; diff --git a/fs/xfs/libxfs/xfs_attr_leaf.h b/fs/xfs/libxfs/xfs_attr_leaf.h index f7dda0c237b0..894124efb421 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.h +++ b/fs/xfs/libxfs/xfs_attr_leaf.h @@ -48,7 +48,8 @@ void	xfs_attr_shortform_create(struct xfs_da_args *args);  void	xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);  int	xfs_attr_shortform_lookup(struct xfs_da_args *args);  int	xfs_attr_shortform_getvalue(struct xfs_da_args *args); -int	xfs_attr_shortform_to_leaf(struct xfs_da_args *args); +int	xfs_attr_shortform_to_leaf(struct xfs_da_args *args, +			struct xfs_buf **leaf_bp);  int	xfs_attr_shortform_remove(struct xfs_da_args *args);  int	xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);  int	xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes); diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 08df809e2315..1bddbba6b80c 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -5136,7 +5136,7 @@ __xfs_bunmapi(  	 * blowing out the transaction with a mix of EFIs and reflink  	 * adjustments.  	 */ -	if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK) +	if (tp && xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK)  		max_len = min(len, xfs_refcount_max_unmap(tp->t_log_res));  	else  		max_len = len; @@ -5662,7 +5662,8 @@ xfs_bmap_collapse_extents(  		*done = true;  		goto del_cursor;  	} -	XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock)); +	XFS_WANT_CORRUPTED_GOTO(mp, !isnullstartblock(got.br_startblock), +				del_cursor);  	new_startoff = got.br_startoff - offset_shift_fsb;  	if (xfs_iext_peek_prev_extent(ifp, &icur, &prev)) { @@ -5767,7 +5768,8 @@ xfs_bmap_insert_extents(  			goto del_cursor;  		}  	} -	XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock)); +	XFS_WANT_CORRUPTED_GOTO(mp, !isnullstartblock(got.br_startblock), +				del_cursor);  	if (stop_fsb >= got.br_startoff + got.br_blockcount) {  		error = -EIO; diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index 072ebfe1d6ae..087fea02c389 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -249,6 +249,10 @@ xfs_defer_trans_roll(  	for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)  		xfs_trans_log_inode(*tp, dop->dop_inodes[i], XFS_ILOG_CORE); +	/* Hold the (previously bjoin'd) buffer locked across the roll. */ +	for (i = 0; i < XFS_DEFER_OPS_NR_BUFS && dop->dop_bufs[i]; i++) +		xfs_trans_dirty_buf(*tp, dop->dop_bufs[i]); +  	trace_xfs_defer_trans_roll((*tp)->t_mountp, dop);  	/* Roll the transaction. */ @@ -264,6 +268,12 @@ xfs_defer_trans_roll(  	for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dop->dop_inodes[i]; i++)  		xfs_trans_ijoin(*tp, dop->dop_inodes[i], 0); +	/* Rejoin the buffers and dirty them so the log moves forward. */ +	for (i = 0; i < XFS_DEFER_OPS_NR_BUFS && dop->dop_bufs[i]; i++) { +		xfs_trans_bjoin(*tp, dop->dop_bufs[i]); +		xfs_trans_bhold(*tp, dop->dop_bufs[i]); +	} +  	return error;  } @@ -295,6 +305,31 @@ xfs_defer_ijoin(  		}  	} +	ASSERT(0); +	return -EFSCORRUPTED; +} + +/* + * Add this buffer to the deferred op.  Each joined buffer is relogged + * each time we roll the transaction. + */ +int +xfs_defer_bjoin( +	struct xfs_defer_ops		*dop, +	struct xfs_buf			*bp) +{ +	int				i; + +	for (i = 0; i < XFS_DEFER_OPS_NR_BUFS; i++) { +		if (dop->dop_bufs[i] == bp) +			return 0; +		else if (dop->dop_bufs[i] == NULL) { +			dop->dop_bufs[i] = bp; +			return 0; +		} +	} + +	ASSERT(0);  	return -EFSCORRUPTED;  } @@ -493,9 +528,7 @@ xfs_defer_init(  	struct xfs_defer_ops		*dop,  	xfs_fsblock_t			*fbp)  { -	dop->dop_committed = false; -	dop->dop_low = false; -	memset(&dop->dop_inodes, 0, sizeof(dop->dop_inodes)); +	memset(dop, 0, sizeof(struct xfs_defer_ops));  	*fbp = NULLFSBLOCK;  	INIT_LIST_HEAD(&dop->dop_intake);  	INIT_LIST_HEAD(&dop->dop_pending); diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h index d4f046dd44bd..045beacdd37d 100644 --- a/fs/xfs/libxfs/xfs_defer.h +++ b/fs/xfs/libxfs/xfs_defer.h @@ -59,6 +59,7 @@ enum xfs_defer_ops_type {  };  #define XFS_DEFER_OPS_NR_INODES	2	/* join up to two inodes */ +#define XFS_DEFER_OPS_NR_BUFS	2	/* join up to two buffers */  struct xfs_defer_ops {  	bool			dop_committed;	/* did any trans commit? */ @@ -66,8 +67,9 @@ struct xfs_defer_ops {  	struct list_head	dop_intake;	/* unlogged pending work */  	struct list_head	dop_pending;	/* logged pending work */ -	/* relog these inodes with each roll */ +	/* relog these with each roll */  	struct xfs_inode	*dop_inodes[XFS_DEFER_OPS_NR_INODES]; +	struct xfs_buf		*dop_bufs[XFS_DEFER_OPS_NR_BUFS];  };  void xfs_defer_add(struct xfs_defer_ops *dop, enum xfs_defer_ops_type type, @@ -77,6 +79,7 @@ void xfs_defer_cancel(struct xfs_defer_ops *dop);  void xfs_defer_init(struct xfs_defer_ops *dop, xfs_fsblock_t *fbp);  bool xfs_defer_has_unfinished_work(struct xfs_defer_ops *dop);  int xfs_defer_ijoin(struct xfs_defer_ops *dop, struct xfs_inode *ip); +int xfs_defer_bjoin(struct xfs_defer_ops *dop, struct xfs_buf *bp);  /* Description of a deferred type. */  struct xfs_defer_op_type { diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index de3f04a98656..3b57ef0f2f76 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -920,8 +920,7 @@ STATIC xfs_agnumber_t  xfs_ialloc_ag_select(  	xfs_trans_t	*tp,		/* transaction pointer */  	xfs_ino_t	parent,		/* parent directory inode number */ -	umode_t		mode,		/* bits set to indicate file type */ -	int		okalloc)	/* ok to allocate more space */ +	umode_t		mode)		/* bits set to indicate file type */  {  	xfs_agnumber_t	agcount;	/* number of ag's in the filesystem */  	xfs_agnumber_t	agno;		/* current ag number */ @@ -978,9 +977,6 @@ xfs_ialloc_ag_select(  			return agno;  		} -		if (!okalloc) -			goto nextag; -  		if (!pag->pagf_init) {  			error = xfs_alloc_pagf_init(mp, tp, agno, flags);  			if (error) @@ -1680,7 +1676,6 @@ xfs_dialloc(  	struct xfs_trans	*tp,  	xfs_ino_t		parent,  	umode_t			mode, -	int			okalloc,  	struct xfs_buf		**IO_agbp,  	xfs_ino_t		*inop)  { @@ -1692,6 +1687,7 @@ xfs_dialloc(  	int			noroom = 0;  	xfs_agnumber_t		start_agno;  	struct xfs_perag	*pag; +	int			okalloc = 1;  	if (*IO_agbp) {  		/* @@ -1707,7 +1703,7 @@ xfs_dialloc(  	 * We do not have an agbp, so select an initial allocation  	 * group for inode allocation.  	 */ -	start_agno = xfs_ialloc_ag_select(tp, parent, mode, okalloc); +	start_agno = xfs_ialloc_ag_select(tp, parent, mode);  	if (start_agno == NULLAGNUMBER) {  		*inop = NULLFSINO;  		return 0; diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h index d2bdcd5e7312..66a8de0b1caa 100644 --- a/fs/xfs/libxfs/xfs_ialloc.h +++ b/fs/xfs/libxfs/xfs_ialloc.h @@ -81,7 +81,6 @@ xfs_dialloc(  	struct xfs_trans *tp,		/* transaction pointer */  	xfs_ino_t	parent,		/* parent inode (directory) */  	umode_t		mode,		/* mode bits for new inode */ -	int		okalloc,	/* ok to allocate more space */  	struct xfs_buf	**agbp,		/* buf for a.g. inode header */  	xfs_ino_t	*inop);		/* inode number allocated */ diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c index 89bf16b4d937..b0f31791c7e6 100644 --- a/fs/xfs/libxfs/xfs_iext_tree.c +++ b/fs/xfs/libxfs/xfs_iext_tree.c @@ -632,8 +632,6 @@ xfs_iext_insert(  	struct xfs_iext_leaf	*new = NULL;  	int			nr_entries, i; -	trace_xfs_iext_insert(ip, cur, state, _RET_IP_); -  	if (ifp->if_height == 0)  		xfs_iext_alloc_root(ifp, cur);  	else if (ifp->if_height == 1) @@ -661,6 +659,8 @@ xfs_iext_insert(  	xfs_iext_set(cur_rec(cur), irec);  	ifp->if_bytes += sizeof(struct xfs_iext_rec); +	trace_xfs_iext_insert(ip, cur, state, _RET_IP_); +  	if (new)  		xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);  } diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c index 585b35d34142..c40d26763075 100644 --- a/fs/xfs/libxfs/xfs_refcount.c +++ b/fs/xfs/libxfs/xfs_refcount.c @@ -1488,27 +1488,12 @@ __xfs_refcount_cow_alloc(  	xfs_extlen_t		aglen,  	struct xfs_defer_ops	*dfops)  { -	int			error; -  	trace_xfs_refcount_cow_increase(rcur->bc_mp, rcur->bc_private.a.agno,  			agbno, aglen);  	/* Add refcount btree reservation */ -	error = xfs_refcount_adjust_cow(rcur, agbno, aglen, +	return xfs_refcount_adjust_cow(rcur, agbno, aglen,  			XFS_REFCOUNT_ADJUST_COW_ALLOC, dfops); -	if (error) -		return error; - -	/* Add rmap entry */ -	if (xfs_sb_version_hasrmapbt(&rcur->bc_mp->m_sb)) { -		error = xfs_rmap_alloc_extent(rcur->bc_mp, dfops, -				rcur->bc_private.a.agno, -				agbno, aglen, XFS_RMAP_OWN_COW); -		if (error) -			return error; -	} - -	return error;  }  /* @@ -1521,27 +1506,12 @@ __xfs_refcount_cow_free(  	xfs_extlen_t		aglen,  	struct xfs_defer_ops	*dfops)  { -	int			error; -  	trace_xfs_refcount_cow_decrease(rcur->bc_mp, rcur->bc_private.a.agno,  			agbno, aglen);  	/* Remove refcount btree reservation */ -	error = xfs_refcount_adjust_cow(rcur, agbno, aglen, +	return xfs_refcount_adjust_cow(rcur, agbno, aglen,  			XFS_REFCOUNT_ADJUST_COW_FREE, dfops); -	if (error) -		return error; - -	/* Remove rmap entry */ -	if (xfs_sb_version_hasrmapbt(&rcur->bc_mp->m_sb)) { -		error = xfs_rmap_free_extent(rcur->bc_mp, dfops, -				rcur->bc_private.a.agno, -				agbno, aglen, XFS_RMAP_OWN_COW); -		if (error) -			return error; -	} - -	return error;  }  /* Record a CoW staging extent in the refcount btree. */ @@ -1552,11 +1522,19 @@ xfs_refcount_alloc_cow_extent(  	xfs_fsblock_t			fsb,  	xfs_extlen_t			len)  { +	int				error; +  	if (!xfs_sb_version_hasreflink(&mp->m_sb))  		return 0; -	return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_ALLOC_COW, +	error = __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_ALLOC_COW,  			fsb, len); +	if (error) +		return error; + +	/* Add rmap entry */ +	return xfs_rmap_alloc_extent(mp, dfops, XFS_FSB_TO_AGNO(mp, fsb), +			XFS_FSB_TO_AGBNO(mp, fsb), len, XFS_RMAP_OWN_COW);  }  /* Forget a CoW staging event in the refcount btree. */ @@ -1567,9 +1545,17 @@ xfs_refcount_free_cow_extent(  	xfs_fsblock_t			fsb,  	xfs_extlen_t			len)  { +	int				error; +  	if (!xfs_sb_version_hasreflink(&mp->m_sb))  		return 0; +	/* Remove rmap entry */ +	error = xfs_rmap_free_extent(mp, dfops, XFS_FSB_TO_AGNO(mp, fsb), +			XFS_FSB_TO_AGBNO(mp, fsb), len, XFS_RMAP_OWN_COW); +	if (error) +		return error; +  	return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_FREE_COW,  			fsb, len);  } diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c index dd019cee1b3b..50db920ceeeb 100644 --- a/fs/xfs/libxfs/xfs_rmap.c +++ b/fs/xfs/libxfs/xfs_rmap.c @@ -368,6 +368,51 @@ xfs_rmap_lookup_le_range(  }  /* + * Perform all the relevant owner checks for a removal op.  If we're doing an + * unknown-owner removal then we have no owner information to check. + */ +static int +xfs_rmap_free_check_owner( +	struct xfs_mount	*mp, +	uint64_t		ltoff, +	struct xfs_rmap_irec	*rec, +	xfs_fsblock_t		bno, +	xfs_filblks_t		len, +	uint64_t		owner, +	uint64_t		offset, +	unsigned int		flags) +{ +	int			error = 0; + +	if (owner == XFS_RMAP_OWN_UNKNOWN) +		return 0; + +	/* Make sure the unwritten flag matches. */ +	XFS_WANT_CORRUPTED_GOTO(mp, (flags & XFS_RMAP_UNWRITTEN) == +			(rec->rm_flags & XFS_RMAP_UNWRITTEN), out); + +	/* Make sure the owner matches what we expect to find in the tree. */ +	XFS_WANT_CORRUPTED_GOTO(mp, owner == rec->rm_owner, out); + +	/* Check the offset, if necessary. */ +	if (XFS_RMAP_NON_INODE_OWNER(owner)) +		goto out; + +	if (flags & XFS_RMAP_BMBT_BLOCK) { +		XFS_WANT_CORRUPTED_GOTO(mp, rec->rm_flags & XFS_RMAP_BMBT_BLOCK, +				out); +	} else { +		XFS_WANT_CORRUPTED_GOTO(mp, rec->rm_offset <= offset, out); +		XFS_WANT_CORRUPTED_GOTO(mp, +				ltoff + rec->rm_blockcount >= offset + len, +				out); +	} + +out: +	return error; +} + +/*   * Find the extent in the rmap btree and remove it.   *   * The record we find should always be an exact match for the extent that we're @@ -444,33 +489,40 @@ xfs_rmap_unmap(  		goto out_done;  	} -	/* Make sure the unwritten flag matches. */ -	XFS_WANT_CORRUPTED_GOTO(mp, (flags & XFS_RMAP_UNWRITTEN) == -			(ltrec.rm_flags & XFS_RMAP_UNWRITTEN), out_error); +	/* +	 * If we're doing an unknown-owner removal for EFI recovery, we expect +	 * to find the full range in the rmapbt or nothing at all.  If we +	 * don't find any rmaps overlapping either end of the range, we're +	 * done.  Hopefully this means that the EFI creator already queued +	 * (and finished) a RUI to remove the rmap. +	 */ +	if (owner == XFS_RMAP_OWN_UNKNOWN && +	    ltrec.rm_startblock + ltrec.rm_blockcount <= bno) { +		struct xfs_rmap_irec    rtrec; + +		error = xfs_btree_increment(cur, 0, &i); +		if (error) +			goto out_error; +		if (i == 0) +			goto out_done; +		error = xfs_rmap_get_rec(cur, &rtrec, &i); +		if (error) +			goto out_error; +		XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error); +		if (rtrec.rm_startblock >= bno + len) +			goto out_done; +	}  	/* Make sure the extent we found covers the entire freeing range. */  	XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_startblock <= bno && -		ltrec.rm_startblock + ltrec.rm_blockcount >= -		bno + len, out_error); +			ltrec.rm_startblock + ltrec.rm_blockcount >= +			bno + len, out_error); -	/* Make sure the owner matches what we expect to find in the tree. */ -	XFS_WANT_CORRUPTED_GOTO(mp, owner == ltrec.rm_owner || -				    XFS_RMAP_NON_INODE_OWNER(owner), out_error); - -	/* Check the offset, if necessary. */ -	if (!XFS_RMAP_NON_INODE_OWNER(owner)) { -		if (flags & XFS_RMAP_BMBT_BLOCK) { -			XFS_WANT_CORRUPTED_GOTO(mp, -					ltrec.rm_flags & XFS_RMAP_BMBT_BLOCK, -					out_error); -		} else { -			XFS_WANT_CORRUPTED_GOTO(mp, -					ltrec.rm_offset <= offset, out_error); -			XFS_WANT_CORRUPTED_GOTO(mp, -					ltoff + ltrec.rm_blockcount >= offset + len, -					out_error); -		} -	} +	/* Check owner information. */ +	error = xfs_rmap_free_check_owner(mp, ltoff, <rec, bno, len, owner, +			offset, flags); +	if (error) +		goto out_error;  	if (ltrec.rm_startblock == bno && ltrec.rm_blockcount == len) {  		/* exact match, simply remove the record from rmap tree */ @@ -664,6 +716,7 @@ xfs_rmap_map(  		flags |= XFS_RMAP_UNWRITTEN;  	trace_xfs_rmap_map(mp, cur->bc_private.a.agno, bno, len,  			unwritten, oinfo); +	ASSERT(!xfs_rmap_should_skip_owner_update(oinfo));  	/*  	 * For the initial lookup, look for an exact match or the left-adjacent diff --git a/fs/xfs/libxfs/xfs_rmap.h b/fs/xfs/libxfs/xfs_rmap.h index 466ede637080..0fcd5b1ba729 100644 --- a/fs/xfs/libxfs/xfs_rmap.h +++ b/fs/xfs/libxfs/xfs_rmap.h @@ -61,7 +61,21 @@ static inline void  xfs_rmap_skip_owner_update(  	struct xfs_owner_info	*oi)  { -	oi->oi_owner = XFS_RMAP_OWN_UNKNOWN; +	xfs_rmap_ag_owner(oi, XFS_RMAP_OWN_NULL); +} + +static inline bool +xfs_rmap_should_skip_owner_update( +	struct xfs_owner_info	*oi) +{ +	return oi->oi_owner == XFS_RMAP_OWN_NULL; +} + +static inline void +xfs_rmap_any_owner_update( +	struct xfs_owner_info	*oi) +{ +	xfs_rmap_ag_owner(oi, XFS_RMAP_OWN_UNKNOWN);  }  /* Reverse mapping functions. */ diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c index 637b7a892313..f120fb20452f 100644 --- a/fs/xfs/scrub/inode.c +++ b/fs/xfs/scrub/inode.c @@ -318,8 +318,20 @@ xfs_scrub_dinode(  	/* di_mode */  	mode = be16_to_cpu(dip->di_mode); -	if (mode & ~(S_IALLUGO | S_IFMT)) +	switch (mode & S_IFMT) { +	case S_IFLNK: +	case S_IFREG: +	case S_IFDIR: +	case S_IFCHR: +	case S_IFBLK: +	case S_IFIFO: +	case S_IFSOCK: +		/* mode is recognized */ +		break; +	default:  		xfs_scrub_ino_set_corrupt(sc, ino, bp); +		break; +	}  	/* v1/v2 fields */  	switch (dip->di_version) { diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 8e58ba842946..3d9037eceaf1 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -107,7 +107,7 @@ xfs_scrub_quota_item(  	unsigned long long		rcount;  	xfs_ino_t			fs_icount; -	offset = id * qi->qi_dqperchunk; +	offset = id / qi->qi_dqperchunk;  	/*  	 * We fed $id and DQNEXT into the xfs_qm_dqget call, which means @@ -207,7 +207,7 @@ xfs_scrub_quota(  	xfs_dqid_t			id = 0;  	uint				dqtype;  	int				nimaps; -	int				error; +	int				error = 0;  	if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))  		return -ENOENT; diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 9c42c4efd01e..ab3aef2ae823 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -46,7 +46,6 @@  #include "scrub/scrub.h"  #include "scrub/common.h"  #include "scrub/trace.h" -#include "scrub/scrub.h"  #include "scrub/btree.h"  /* diff --git a/fs/xfs/scrub/trace.c b/fs/xfs/scrub/trace.c index 472080e75788..86daed0e3a45 100644 --- a/fs/xfs/scrub/trace.c +++ b/fs/xfs/scrub/trace.c @@ -26,7 +26,6 @@  #include "xfs_mount.h"  #include "xfs_defer.h"  #include "xfs_da_format.h" -#include "xfs_defer.h"  #include "xfs_inode.h"  #include "xfs_btree.h"  #include "xfs_trans.h" diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index a3eeaba156c5..4fc526a27a94 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -399,7 +399,7 @@ xfs_map_blocks(  	       (ip->i_df.if_flags & XFS_IFEXTENTS));  	ASSERT(offset <= mp->m_super->s_maxbytes); -	if (offset + count > mp->m_super->s_maxbytes) +	if (offset > mp->m_super->s_maxbytes - count)  		count = mp->m_super->s_maxbytes - offset;  	end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);  	offset_fsb = XFS_B_TO_FSBT(mp, offset); @@ -896,13 +896,13 @@ xfs_writepage_map(  	struct writeback_control *wbc,  	struct inode		*inode,  	struct page		*page, -	loff_t			offset, -	uint64_t              end_offset) +	uint64_t		end_offset)  {  	LIST_HEAD(submit_list);  	struct xfs_ioend	*ioend, *next;  	struct buffer_head	*bh, *head;  	ssize_t			len = i_blocksize(inode); +	uint64_t		offset;  	int			error = 0;  	int			count = 0;  	int			uptodate = 1; @@ -1146,7 +1146,7 @@ xfs_do_writepage(  		end_offset = offset;  	} -	return xfs_writepage_map(wpc, wbc, inode, page, offset, end_offset); +	return xfs_writepage_map(wpc, wbc, inode, page, end_offset);  redirty:  	redirty_page_for_writepage(wbc, page); @@ -1265,7 +1265,7 @@ xfs_map_trim_size(  	if (mapping_size > size)  		mapping_size = size;  	if (offset < i_size_read(inode) && -	    offset + mapping_size >= i_size_read(inode)) { +	    (xfs_ufsize_t)offset + mapping_size >= i_size_read(inode)) {  		/* limit mapping to block that spans EOF */  		mapping_size = roundup_64(i_size_read(inode) - offset,  					  i_blocksize(inode)); @@ -1312,7 +1312,7 @@ xfs_get_blocks(  	lockmode = xfs_ilock_data_map_shared(ip);  	ASSERT(offset <= mp->m_super->s_maxbytes); -	if (offset + size > mp->m_super->s_maxbytes) +	if (offset > mp->m_super->s_maxbytes - size)  		size = mp->m_super->s_maxbytes - offset;  	end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + size);  	offset_fsb = XFS_B_TO_FSBT(mp, offset); diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c index dd136f7275e4..e5fb008d75e8 100644 --- a/fs/xfs/xfs_bmap_item.c +++ b/fs/xfs/xfs_bmap_item.c @@ -389,7 +389,8 @@ xfs_bud_init(  int  xfs_bui_recover(  	struct xfs_mount		*mp, -	struct xfs_bui_log_item		*buip) +	struct xfs_bui_log_item		*buip, +	struct xfs_defer_ops		*dfops)  {  	int				error = 0;  	unsigned int			bui_type; @@ -404,9 +405,7 @@ xfs_bui_recover(  	xfs_exntst_t			state;  	struct xfs_trans		*tp;  	struct xfs_inode		*ip = NULL; -	struct xfs_defer_ops		dfops;  	struct xfs_bmbt_irec		irec; -	xfs_fsblock_t			firstfsb;  	ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags)); @@ -464,7 +463,6 @@ xfs_bui_recover(  	if (VFS_I(ip)->i_nlink == 0)  		xfs_iflags_set(ip, XFS_IRECOVERY); -	xfs_defer_init(&dfops, &firstfsb);  	/* Process deferred bmap item. */  	state = (bmap->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ? @@ -479,16 +477,16 @@ xfs_bui_recover(  		break;  	default:  		error = -EFSCORRUPTED; -		goto err_dfops; +		goto err_inode;  	}  	xfs_trans_ijoin(tp, ip, 0);  	count = bmap->me_len; -	error = xfs_trans_log_finish_bmap_update(tp, budp, &dfops, type, +	error = xfs_trans_log_finish_bmap_update(tp, budp, dfops, type,  			ip, whichfork, bmap->me_startoff,  			bmap->me_startblock, &count, state);  	if (error) -		goto err_dfops; +		goto err_inode;  	if (count > 0) {  		ASSERT(type == XFS_BMAP_UNMAP); @@ -496,16 +494,11 @@ xfs_bui_recover(  		irec.br_blockcount = count;  		irec.br_startoff = bmap->me_startoff;  		irec.br_state = state; -		error = xfs_bmap_unmap_extent(tp->t_mountp, &dfops, ip, &irec); +		error = xfs_bmap_unmap_extent(tp->t_mountp, dfops, ip, &irec);  		if (error) -			goto err_dfops; +			goto err_inode;  	} -	/* Finish transaction, free inodes. */ -	error = xfs_defer_finish(&tp, &dfops); -	if (error) -		goto err_dfops; -  	set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);  	error = xfs_trans_commit(tp);  	xfs_iunlock(ip, XFS_ILOCK_EXCL); @@ -513,8 +506,6 @@ xfs_bui_recover(  	return error; -err_dfops: -	xfs_defer_cancel(&dfops);  err_inode:  	xfs_trans_cancel(tp);  	if (ip) { diff --git a/fs/xfs/xfs_bmap_item.h b/fs/xfs/xfs_bmap_item.h index c867daae4a3c..24b354a2c836 100644 --- a/fs/xfs/xfs_bmap_item.h +++ b/fs/xfs/xfs_bmap_item.h @@ -93,6 +93,7 @@ struct xfs_bud_log_item *xfs_bud_init(struct xfs_mount *,  		struct xfs_bui_log_item *);  void xfs_bui_item_free(struct xfs_bui_log_item *);  void xfs_bui_release(struct xfs_bui_log_item *); -int xfs_bui_recover(struct xfs_mount *mp, struct xfs_bui_log_item *buip); +int xfs_bui_recover(struct xfs_mount *mp, struct xfs_bui_log_item *buip, +		struct xfs_defer_ops *dfops);  #endif	/* __XFS_BMAP_ITEM_H__ */ diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 4db6e8d780f6..4c6e86d861fd 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1815,22 +1815,27 @@ xfs_alloc_buftarg(  	btp->bt_daxdev = dax_dev;  	if (xfs_setsize_buftarg_early(btp, bdev)) -		goto error; +		goto error_free;  	if (list_lru_init(&btp->bt_lru)) -		goto error; +		goto error_free;  	if (percpu_counter_init(&btp->bt_io_count, 0, GFP_KERNEL)) -		goto error; +		goto error_lru;  	btp->bt_shrinker.count_objects = xfs_buftarg_shrink_count;  	btp->bt_shrinker.scan_objects = xfs_buftarg_shrink_scan;  	btp->bt_shrinker.seeks = DEFAULT_SEEKS;  	btp->bt_shrinker.flags = SHRINKER_NUMA_AWARE; -	register_shrinker(&btp->bt_shrinker); +	if (register_shrinker(&btp->bt_shrinker)) +		goto error_pcpu;  	return btp; -error: +error_pcpu: +	percpu_counter_destroy(&btp->bt_io_count); +error_lru: +	list_lru_destroy(&btp->bt_lru); +error_free:  	kmem_free(btp);  	return NULL;  } diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index d57c2db64e59..f248708c10ff 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -970,14 +970,22 @@ xfs_qm_dqflush_done(  	 * holding the lock before removing the dquot from the AIL.  	 */  	if ((lip->li_flags & XFS_LI_IN_AIL) && -	    lip->li_lsn == qip->qli_flush_lsn) { +	    ((lip->li_lsn == qip->qli_flush_lsn) || +	     (lip->li_flags & XFS_LI_FAILED))) {  		/* xfs_trans_ail_delete() drops the AIL lock. */  		spin_lock(&ailp->xa_lock); -		if (lip->li_lsn == qip->qli_flush_lsn) +		if (lip->li_lsn == qip->qli_flush_lsn) {  			xfs_trans_ail_delete(ailp, lip, SHUTDOWN_CORRUPT_INCORE); -		else +		} else { +			/* +			 * Clear the failed state since we are about to drop the +			 * flush lock +			 */ +			if (lip->li_flags & XFS_LI_FAILED) +				xfs_clear_li_failed(lip);  			spin_unlock(&ailp->xa_lock); +		}  	}  	/* diff --git a/fs/xfs/xfs_dquot_item.c b/fs/xfs/xfs_dquot_item.c index 2c7a1629e064..664dea105e76 100644 --- a/fs/xfs/xfs_dquot_item.c +++ b/fs/xfs/xfs_dquot_item.c @@ -137,6 +137,26 @@ xfs_qm_dqunpin_wait(  	wait_event(dqp->q_pinwait, (atomic_read(&dqp->q_pincount) == 0));  } +/* + * Callback used to mark a buffer with XFS_LI_FAILED when items in the buffer + * have been failed during writeback + * + * this informs the AIL that the dquot is already flush locked on the next push, + * and acquires a hold on the buffer to ensure that it isn't reclaimed before + * dirty data makes it to disk. + */ +STATIC void +xfs_dquot_item_error( +	struct xfs_log_item	*lip, +	struct xfs_buf		*bp) +{ +	struct xfs_dquot	*dqp; + +	dqp = DQUOT_ITEM(lip)->qli_dquot; +	ASSERT(!completion_done(&dqp->q_flush)); +	xfs_set_li_failed(lip, bp); +} +  STATIC uint  xfs_qm_dquot_logitem_push(  	struct xfs_log_item	*lip, @@ -144,13 +164,28 @@ xfs_qm_dquot_logitem_push(  					      __acquires(&lip->li_ailp->xa_lock)  {  	struct xfs_dquot	*dqp = DQUOT_ITEM(lip)->qli_dquot; -	struct xfs_buf		*bp = NULL; +	struct xfs_buf		*bp = lip->li_buf;  	uint			rval = XFS_ITEM_SUCCESS;  	int			error;  	if (atomic_read(&dqp->q_pincount) > 0)  		return XFS_ITEM_PINNED; +	/* +	 * The buffer containing this item failed to be written back +	 * previously. Resubmit the buffer for IO +	 */ +	if (lip->li_flags & XFS_LI_FAILED) { +		if (!xfs_buf_trylock(bp)) +			return XFS_ITEM_LOCKED; + +		if (!xfs_buf_resubmit_failed_buffers(bp, lip, buffer_list)) +			rval = XFS_ITEM_FLUSHING; + +		xfs_buf_unlock(bp); +		return rval; +	} +  	if (!xfs_dqlock_nowait(dqp))  		return XFS_ITEM_LOCKED; @@ -242,7 +277,8 @@ static const struct xfs_item_ops xfs_dquot_item_ops = {  	.iop_unlock	= xfs_qm_dquot_logitem_unlock,  	.iop_committed	= xfs_qm_dquot_logitem_committed,  	.iop_push	= xfs_qm_dquot_logitem_push, -	.iop_committing = xfs_qm_dquot_logitem_committing +	.iop_committing = xfs_qm_dquot_logitem_committing, +	.iop_error	= xfs_dquot_item_error  };  /* diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 44f8c5451210..64da90655e95 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -538,7 +538,7 @@ xfs_efi_recover(  		return error;  	efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents); -	xfs_rmap_skip_owner_update(&oinfo); +	xfs_rmap_any_owner_update(&oinfo);  	for (i = 0; i < efip->efi_format.efi_nextents; i++) {  		extp = &efip->efi_format.efi_extents[i];  		error = xfs_trans_free_extent(tp, efdp, extp->ext_start, diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 8f22fc579dbb..60a2e128cb6a 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -571,6 +571,11 @@ xfs_growfs_data_private(  		 * this doesn't actually exist in the rmap btree.  		 */  		xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_NULL); +		error = xfs_rmap_free(tp, bp, agno, +				be32_to_cpu(agf->agf_length) - new, +				new, &oinfo); +		if (error) +			goto error0;  		error = xfs_free_extent(tp,  				XFS_AGB_TO_FSB(mp, agno,  					be32_to_cpu(agf->agf_length) - new), diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 43005fbe8b1e..3861d61fb265 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -870,7 +870,7 @@ xfs_eofblocks_worker(   * based on the 'speculative_cow_prealloc_lifetime' tunable (5m by default).   * (We'll just piggyback on the post-EOF prealloc space workqueue.)   */ -STATIC void +void  xfs_queue_cowblocks(  	struct xfs_mount *mp)  { @@ -1536,8 +1536,23 @@ xfs_inode_free_quota_eofblocks(  	return __xfs_inode_free_quota_eofblocks(ip, xfs_icache_free_eofblocks);  } +static inline unsigned long +xfs_iflag_for_tag( +	int		tag) +{ +	switch (tag) { +	case XFS_ICI_EOFBLOCKS_TAG: +		return XFS_IEOFBLOCKS; +	case XFS_ICI_COWBLOCKS_TAG: +		return XFS_ICOWBLOCKS; +	default: +		ASSERT(0); +		return 0; +	} +} +  static void -__xfs_inode_set_eofblocks_tag( +__xfs_inode_set_blocks_tag(  	xfs_inode_t	*ip,  	void		(*execute)(struct xfs_mount *mp),  	void		(*set_tp)(struct xfs_mount *mp, xfs_agnumber_t agno, @@ -1552,10 +1567,10 @@ __xfs_inode_set_eofblocks_tag(  	 * Don't bother locking the AG and looking up in the radix trees  	 * if we already know that we have the tag set.  	 */ -	if (ip->i_flags & XFS_IEOFBLOCKS) +	if (ip->i_flags & xfs_iflag_for_tag(tag))  		return;  	spin_lock(&ip->i_flags_lock); -	ip->i_flags |= XFS_IEOFBLOCKS; +	ip->i_flags |= xfs_iflag_for_tag(tag);  	spin_unlock(&ip->i_flags_lock);  	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); @@ -1587,13 +1602,13 @@ xfs_inode_set_eofblocks_tag(  	xfs_inode_t	*ip)  {  	trace_xfs_inode_set_eofblocks_tag(ip); -	return __xfs_inode_set_eofblocks_tag(ip, xfs_queue_eofblocks, +	return __xfs_inode_set_blocks_tag(ip, xfs_queue_eofblocks,  			trace_xfs_perag_set_eofblocks,  			XFS_ICI_EOFBLOCKS_TAG);  }  static void -__xfs_inode_clear_eofblocks_tag( +__xfs_inode_clear_blocks_tag(  	xfs_inode_t	*ip,  	void		(*clear_tp)(struct xfs_mount *mp, xfs_agnumber_t agno,  				    int error, unsigned long caller_ip), @@ -1603,7 +1618,7 @@ __xfs_inode_clear_eofblocks_tag(  	struct xfs_perag *pag;  	spin_lock(&ip->i_flags_lock); -	ip->i_flags &= ~XFS_IEOFBLOCKS; +	ip->i_flags &= ~xfs_iflag_for_tag(tag);  	spin_unlock(&ip->i_flags_lock);  	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); @@ -1630,7 +1645,7 @@ xfs_inode_clear_eofblocks_tag(  	xfs_inode_t	*ip)  {  	trace_xfs_inode_clear_eofblocks_tag(ip); -	return __xfs_inode_clear_eofblocks_tag(ip, +	return __xfs_inode_clear_blocks_tag(ip,  			trace_xfs_perag_clear_eofblocks, XFS_ICI_EOFBLOCKS_TAG);  } @@ -1724,7 +1739,7 @@ xfs_inode_set_cowblocks_tag(  	xfs_inode_t	*ip)  {  	trace_xfs_inode_set_cowblocks_tag(ip); -	return __xfs_inode_set_eofblocks_tag(ip, xfs_queue_cowblocks, +	return __xfs_inode_set_blocks_tag(ip, xfs_queue_cowblocks,  			trace_xfs_perag_set_cowblocks,  			XFS_ICI_COWBLOCKS_TAG);  } @@ -1734,6 +1749,6 @@ xfs_inode_clear_cowblocks_tag(  	xfs_inode_t	*ip)  {  	trace_xfs_inode_clear_cowblocks_tag(ip); -	return __xfs_inode_clear_eofblocks_tag(ip, +	return __xfs_inode_clear_blocks_tag(ip,  			trace_xfs_perag_clear_cowblocks, XFS_ICI_COWBLOCKS_TAG);  } diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h index bff4d85e5498..d4a77588eca1 100644 --- a/fs/xfs/xfs_icache.h +++ b/fs/xfs/xfs_icache.h @@ -81,6 +81,7 @@ void xfs_inode_clear_cowblocks_tag(struct xfs_inode *ip);  int xfs_icache_free_cowblocks(struct xfs_mount *, struct xfs_eofblocks *);  int xfs_inode_free_quota_cowblocks(struct xfs_inode *ip);  void xfs_cowblocks_worker(struct work_struct *); +void xfs_queue_cowblocks(struct xfs_mount *);  int xfs_inode_ag_iterator(struct xfs_mount *mp,  	int (*execute)(struct xfs_inode *ip, int flags, void *args), diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 61d1cb7dc10d..6f95bdb408ce 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -749,7 +749,6 @@ xfs_ialloc(  	xfs_nlink_t	nlink,  	dev_t		rdev,  	prid_t		prid, -	int		okalloc,  	xfs_buf_t	**ialloc_context,  	xfs_inode_t	**ipp)  { @@ -765,7 +764,7 @@ xfs_ialloc(  	 * Call the space management code to pick  	 * the on-disk inode to be allocated.  	 */ -	error = xfs_dialloc(tp, pip ? pip->i_ino : 0, mode, okalloc, +	error = xfs_dialloc(tp, pip ? pip->i_ino : 0, mode,  			    ialloc_context, &ino);  	if (error)  		return error; @@ -957,7 +956,6 @@ xfs_dir_ialloc(  	xfs_nlink_t	nlink,  	dev_t		rdev,  	prid_t		prid,		/* project id */ -	int		okalloc,	/* ok to allocate new space */  	xfs_inode_t	**ipp,		/* pointer to inode; it will be  					   locked. */  	int		*committed) @@ -988,8 +986,8 @@ xfs_dir_ialloc(  	 * transaction commit so that no other process can steal  	 * the inode(s) that we've just allocated.  	 */ -	code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid, okalloc, -			  &ialloc_context, &ip); +	code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid, &ialloc_context, +			&ip);  	/*  	 * Return an error if we were unable to allocate a new inode. @@ -1061,7 +1059,7 @@ xfs_dir_ialloc(  		 * this call should always succeed.  		 */  		code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid, -				  okalloc, &ialloc_context, &ip); +				  &ialloc_context, &ip);  		/*  		 * If we get an error at this point, return to the caller @@ -1182,11 +1180,6 @@ xfs_create(  		xfs_flush_inodes(mp);  		error = xfs_trans_alloc(mp, tres, resblks, 0, 0, &tp);  	} -	if (error == -ENOSPC) { -		/* No space at all so try a "no-allocation" reservation */ -		resblks = 0; -		error = xfs_trans_alloc(mp, tres, 0, 0, 0, &tp); -	}  	if (error)  		goto out_release_inode; @@ -1203,19 +1196,13 @@ xfs_create(  	if (error)  		goto out_trans_cancel; -	if (!resblks) { -		error = xfs_dir_canenter(tp, dp, name); -		if (error) -			goto out_trans_cancel; -	} -  	/*  	 * A newly created regular or special file just has one directory  	 * entry pointing to them, but a directory also the "." entry  	 * pointing to itself.  	 */ -	error = xfs_dir_ialloc(&tp, dp, mode, is_dir ? 2 : 1, rdev, -			       prid, resblks > 0, &ip, NULL); +	error = xfs_dir_ialloc(&tp, dp, mode, is_dir ? 2 : 1, rdev, prid, &ip, +			NULL);  	if (error)  		goto out_trans_cancel; @@ -1340,11 +1327,6 @@ xfs_create_tmpfile(  	tres = &M_RES(mp)->tr_create_tmpfile;  	error = xfs_trans_alloc(mp, tres, resblks, 0, 0, &tp); -	if (error == -ENOSPC) { -		/* No space at all so try a "no-allocation" reservation */ -		resblks = 0; -		error = xfs_trans_alloc(mp, tres, 0, 0, 0, &tp); -	}  	if (error)  		goto out_release_inode; @@ -1353,8 +1335,7 @@ xfs_create_tmpfile(  	if (error)  		goto out_trans_cancel; -	error = xfs_dir_ialloc(&tp, dp, mode, 1, 0, -				prid, resblks > 0, &ip, NULL); +	error = xfs_dir_ialloc(&tp, dp, mode, 1, 0, prid, &ip, NULL);  	if (error)  		goto out_trans_cancel; @@ -1506,6 +1487,24 @@ xfs_link(  	return error;  } +/* Clear the reflink flag and the cowblocks tag if possible. */ +static void +xfs_itruncate_clear_reflink_flags( +	struct xfs_inode	*ip) +{ +	struct xfs_ifork	*dfork; +	struct xfs_ifork	*cfork; + +	if (!xfs_is_reflink_inode(ip)) +		return; +	dfork = XFS_IFORK_PTR(ip, XFS_DATA_FORK); +	cfork = XFS_IFORK_PTR(ip, XFS_COW_FORK); +	if (dfork->if_bytes == 0 && cfork->if_bytes == 0) +		ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK; +	if (cfork->if_bytes == 0) +		xfs_inode_clear_cowblocks_tag(ip); +} +  /*   * Free up the underlying blocks past new_size.  The new size must be smaller   * than the current size.  This routine can be used both for the attribute and @@ -1602,15 +1601,7 @@ xfs_itruncate_extents(  	if (error)  		goto out; -	/* -	 * Clear the reflink flag if there are no data fork blocks and -	 * there are no extents staged in the cow fork. -	 */ -	if (xfs_is_reflink_inode(ip) && ip->i_cnextents == 0) { -		if (ip->i_d.di_nblocks == 0) -			ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK; -		xfs_inode_clear_cowblocks_tag(ip); -	} +	xfs_itruncate_clear_reflink_flags(ip);  	/*  	 * Always re-log the inode so that our permanent transaction can keep @@ -2401,6 +2392,24 @@ retry:  }  /* + * Free any local-format buffers sitting around before we reset to + * extents format. + */ +static inline void +xfs_ifree_local_data( +	struct xfs_inode	*ip, +	int			whichfork) +{ +	struct xfs_ifork	*ifp; + +	if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL) +		return; + +	ifp = XFS_IFORK_PTR(ip, whichfork); +	xfs_idata_realloc(ip, -ifp->if_bytes, whichfork); +} + +/*   * This is called to return an inode to the inode free list.   * The inode should already be truncated to 0 length and have   * no pages associated with it.  This routine also assumes that @@ -2437,6 +2446,9 @@ xfs_ifree(  	if (error)  		return error; +	xfs_ifree_local_data(ip, XFS_DATA_FORK); +	xfs_ifree_local_data(ip, XFS_ATTR_FORK); +  	VFS_I(ip)->i_mode = 0;		/* mark incore inode as free */  	ip->i_d.di_flags = 0;  	ip->i_d.di_dmevmask = 0; diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index cc13c3763721..d383e392ec9d 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -232,6 +232,7 @@ static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)   * log recovery to replay a bmap operation on the inode.   */  #define XFS_IRECOVERY		(1 << 11) +#define XFS_ICOWBLOCKS		(1 << 12)/* has the cowblocks tag set */  /*   * Per-lifetime flags need to be reset when re-using a reclaimable inode during @@ -428,7 +429,7 @@ xfs_extlen_t	xfs_get_extsz_hint(struct xfs_inode *ip);  xfs_extlen_t	xfs_get_cowextsz_hint(struct xfs_inode *ip);  int		xfs_dir_ialloc(struct xfs_trans **, struct xfs_inode *, umode_t, -			       xfs_nlink_t, dev_t, prid_t, int, +			       xfs_nlink_t, dev_t, prid_t,  			       struct xfs_inode **, int *);  /* from xfs_file.c */ diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 33eb4fb2e3fd..66e1edbfb2b2 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -1006,7 +1006,7 @@ xfs_file_iomap_begin(  	}  	ASSERT(offset <= mp->m_super->s_maxbytes); -	if ((xfs_fsize_t)offset + length > mp->m_super->s_maxbytes) +	if (offset > mp->m_super->s_maxbytes - length)  		length = mp->m_super->s_maxbytes - offset;  	offset_fsb = XFS_B_TO_FSBT(mp, offset);  	end_fsb = XFS_B_TO_FSB(mp, offset + length); @@ -1213,7 +1213,7 @@ xfs_xattr_iomap_begin(  	ASSERT(ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL);  	error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap, -			       &nimaps, XFS_BMAPI_ENTIRE | XFS_BMAPI_ATTRFORK); +			       &nimaps, XFS_BMAPI_ATTRFORK);  out_unlock:  	xfs_iunlock(ip, lockmode); diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 38d4227895ae..a503af96d780 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -781,17 +781,17 @@ xfs_log_mount_finish(  	 * something to an unlinked inode, the irele won't cause  	 * premature truncation and freeing of the inode, which results  	 * in log recovery failure.  We have to evict the unreferenced -	 * lru inodes after clearing MS_ACTIVE because we don't +	 * lru inodes after clearing SB_ACTIVE because we don't  	 * otherwise clean up the lru if there's a subsequent failure in  	 * xfs_mountfs, which leads to us leaking the inodes if nothing  	 * else (e.g. quotacheck) references the inodes before the  	 * mount failure occurs.  	 */ -	mp->m_super->s_flags |= MS_ACTIVE; +	mp->m_super->s_flags |= SB_ACTIVE;  	error = xlog_recover_finish(mp->m_log);  	if (!error)  		xfs_log_work_queue(mp); -	mp->m_super->s_flags &= ~MS_ACTIVE; +	mp->m_super->s_flags &= ~SB_ACTIVE;  	evict_inodes(mp->m_super);  	/* diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 87b1c331f9eb..28d1abfe835e 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -24,6 +24,7 @@  #include "xfs_bit.h"  #include "xfs_sb.h"  #include "xfs_mount.h" +#include "xfs_defer.h"  #include "xfs_da_format.h"  #include "xfs_da_btree.h"  #include "xfs_inode.h" @@ -4716,7 +4717,8 @@ STATIC int  xlog_recover_process_cui(  	struct xfs_mount		*mp,  	struct xfs_ail			*ailp, -	struct xfs_log_item		*lip) +	struct xfs_log_item		*lip, +	struct xfs_defer_ops		*dfops)  {  	struct xfs_cui_log_item		*cuip;  	int				error; @@ -4729,7 +4731,7 @@ xlog_recover_process_cui(  		return 0;  	spin_unlock(&ailp->xa_lock); -	error = xfs_cui_recover(mp, cuip); +	error = xfs_cui_recover(mp, cuip, dfops);  	spin_lock(&ailp->xa_lock);  	return error; @@ -4756,7 +4758,8 @@ STATIC int  xlog_recover_process_bui(  	struct xfs_mount		*mp,  	struct xfs_ail			*ailp, -	struct xfs_log_item		*lip) +	struct xfs_log_item		*lip, +	struct xfs_defer_ops		*dfops)  {  	struct xfs_bui_log_item		*buip;  	int				error; @@ -4769,7 +4772,7 @@ xlog_recover_process_bui(  		return 0;  	spin_unlock(&ailp->xa_lock); -	error = xfs_bui_recover(mp, buip); +	error = xfs_bui_recover(mp, buip, dfops);  	spin_lock(&ailp->xa_lock);  	return error; @@ -4805,6 +4808,46 @@ static inline bool xlog_item_is_intent(struct xfs_log_item *lip)  	}  } +/* Take all the collected deferred ops and finish them in order. */ +static int +xlog_finish_defer_ops( +	struct xfs_mount	*mp, +	struct xfs_defer_ops	*dfops) +{ +	struct xfs_trans	*tp; +	int64_t			freeblks; +	uint			resblks; +	int			error; + +	/* +	 * We're finishing the defer_ops that accumulated as a result of +	 * recovering unfinished intent items during log recovery.  We +	 * reserve an itruncate transaction because it is the largest +	 * permanent transaction type.  Since we're the only user of the fs +	 * right now, take 93% (15/16) of the available free blocks.  Use +	 * weird math to avoid a 64-bit division. +	 */ +	freeblks = percpu_counter_sum(&mp->m_fdblocks); +	if (freeblks <= 0) +		return -ENOSPC; +	resblks = min_t(int64_t, UINT_MAX, freeblks); +	resblks = (resblks * 15) >> 4; +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, resblks, +			0, XFS_TRANS_RESERVE, &tp); +	if (error) +		return error; + +	error = xfs_defer_finish(&tp, dfops); +	if (error) +		goto out_cancel; + +	return xfs_trans_commit(tp); + +out_cancel: +	xfs_trans_cancel(tp); +	return error; +} +  /*   * When this is called, all of the log intent items which did not have   * corresponding log done items should be in the AIL.  What we do now @@ -4825,10 +4868,12 @@ STATIC int  xlog_recover_process_intents(  	struct xlog		*log)  { -	struct xfs_log_item	*lip; -	int			error = 0; +	struct xfs_defer_ops	dfops;  	struct xfs_ail_cursor	cur; +	struct xfs_log_item	*lip;  	struct xfs_ail		*ailp; +	xfs_fsblock_t		firstfsb; +	int			error = 0;  #if defined(DEBUG) || defined(XFS_WARN)  	xfs_lsn_t		last_lsn;  #endif @@ -4839,6 +4884,7 @@ xlog_recover_process_intents(  #if defined(DEBUG) || defined(XFS_WARN)  	last_lsn = xlog_assign_lsn(log->l_curr_cycle, log->l_curr_block);  #endif +	xfs_defer_init(&dfops, &firstfsb);  	while (lip != NULL) {  		/*  		 * We're done when we see something other than an intent. @@ -4859,6 +4905,12 @@ xlog_recover_process_intents(  		 */  		ASSERT(XFS_LSN_CMP(last_lsn, lip->li_lsn) >= 0); +		/* +		 * NOTE: If your intent processing routine can create more +		 * deferred ops, you /must/ attach them to the dfops in this +		 * routine or else those subsequent intents will get +		 * replayed in the wrong order! +		 */  		switch (lip->li_type) {  		case XFS_LI_EFI:  			error = xlog_recover_process_efi(log->l_mp, ailp, lip); @@ -4867,10 +4919,12 @@ xlog_recover_process_intents(  			error = xlog_recover_process_rui(log->l_mp, ailp, lip);  			break;  		case XFS_LI_CUI: -			error = xlog_recover_process_cui(log->l_mp, ailp, lip); +			error = xlog_recover_process_cui(log->l_mp, ailp, lip, +					&dfops);  			break;  		case XFS_LI_BUI: -			error = xlog_recover_process_bui(log->l_mp, ailp, lip); +			error = xlog_recover_process_bui(log->l_mp, ailp, lip, +					&dfops);  			break;  		}  		if (error) @@ -4880,6 +4934,11 @@ xlog_recover_process_intents(  out:  	xfs_trans_ail_cursor_done(&cur);  	spin_unlock(&ailp->xa_lock); +	if (error) +		xfs_defer_cancel(&dfops); +	else +		error = xlog_finish_defer_ops(log->l_mp, &dfops); +  	return error;  } diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 010a13a201aa..b897b11afb2c 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -48,7 +48,7 @@  STATIC int	xfs_qm_init_quotainos(xfs_mount_t *);  STATIC int	xfs_qm_init_quotainfo(xfs_mount_t *); - +STATIC void	xfs_qm_destroy_quotainos(xfs_quotainfo_t *qi);  STATIC void	xfs_qm_dqfree_one(struct xfs_dquot *dqp);  /*   * We use the batch lookup interface to iterate over the dquots as it @@ -695,9 +695,17 @@ xfs_qm_init_quotainfo(  	qinf->qi_shrinker.scan_objects = xfs_qm_shrink_scan;  	qinf->qi_shrinker.seeks = DEFAULT_SEEKS;  	qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE; -	register_shrinker(&qinf->qi_shrinker); + +	error = register_shrinker(&qinf->qi_shrinker); +	if (error) +		goto out_free_inos; +  	return 0; +out_free_inos: +	mutex_destroy(&qinf->qi_quotaofflock); +	mutex_destroy(&qinf->qi_tree_lock); +	xfs_qm_destroy_quotainos(qinf);  out_free_lru:  	list_lru_destroy(&qinf->qi_lru);  out_free_qinf: @@ -706,7 +714,6 @@ out_free_qinf:  	return error;  } -  /*   * Gets called when unmounting a filesystem or when all quotas get   * turned off. @@ -723,19 +730,8 @@ xfs_qm_destroy_quotainfo(  	unregister_shrinker(&qi->qi_shrinker);  	list_lru_destroy(&qi->qi_lru); - -	if (qi->qi_uquotaip) { -		IRELE(qi->qi_uquotaip); -		qi->qi_uquotaip = NULL; /* paranoia */ -	} -	if (qi->qi_gquotaip) { -		IRELE(qi->qi_gquotaip); -		qi->qi_gquotaip = NULL; -	} -	if (qi->qi_pquotaip) { -		IRELE(qi->qi_pquotaip); -		qi->qi_pquotaip = NULL; -	} +	xfs_qm_destroy_quotainos(qi); +	mutex_destroy(&qi->qi_tree_lock);  	mutex_destroy(&qi->qi_quotaofflock);  	kmem_free(qi);  	mp->m_quotainfo = NULL; @@ -793,8 +789,8 @@ xfs_qm_qino_alloc(  		return error;  	if (need_alloc) { -		error = xfs_dir_ialloc(&tp, NULL, S_IFREG, 1, 0, 0, 1, ip, -								&committed); +		error = xfs_dir_ialloc(&tp, NULL, S_IFREG, 1, 0, 0, ip, +				&committed);  		if (error) {  			xfs_trans_cancel(tp);  			return error; @@ -1600,6 +1596,24 @@ error_rele:  }  STATIC void +xfs_qm_destroy_quotainos( +	xfs_quotainfo_t	*qi) +{ +	if (qi->qi_uquotaip) { +		IRELE(qi->qi_uquotaip); +		qi->qi_uquotaip = NULL; /* paranoia */ +	} +	if (qi->qi_gquotaip) { +		IRELE(qi->qi_gquotaip); +		qi->qi_gquotaip = NULL; +	} +	if (qi->qi_pquotaip) { +		IRELE(qi->qi_pquotaip); +		qi->qi_pquotaip = NULL; +	} +} + +STATIC void  xfs_qm_dqfree_one(  	struct xfs_dquot	*dqp)  { diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c index 8f2e2fac4255..3a55d6fc271b 100644 --- a/fs/xfs/xfs_refcount_item.c +++ b/fs/xfs/xfs_refcount_item.c @@ -393,7 +393,8 @@ xfs_cud_init(  int  xfs_cui_recover(  	struct xfs_mount		*mp, -	struct xfs_cui_log_item		*cuip) +	struct xfs_cui_log_item		*cuip, +	struct xfs_defer_ops		*dfops)  {  	int				i;  	int				error = 0; @@ -405,11 +406,9 @@ xfs_cui_recover(  	struct xfs_trans		*tp;  	struct xfs_btree_cur		*rcur = NULL;  	enum xfs_refcount_intent_type	type; -	xfs_fsblock_t			firstfsb;  	xfs_fsblock_t			new_fsb;  	xfs_extlen_t			new_len;  	struct xfs_bmbt_irec		irec; -	struct xfs_defer_ops		dfops;  	bool				requeue_only = false;  	ASSERT(!test_bit(XFS_CUI_RECOVERED, &cuip->cui_flags)); @@ -465,7 +464,6 @@ xfs_cui_recover(  		return error;  	cudp = xfs_trans_get_cud(tp, cuip); -	xfs_defer_init(&dfops, &firstfsb);  	for (i = 0; i < cuip->cui_format.cui_nextents; i++) {  		refc = &cuip->cui_format.cui_extents[i];  		refc_type = refc->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK; @@ -485,7 +483,7 @@ xfs_cui_recover(  			new_len = refc->pe_len;  		} else  			error = xfs_trans_log_finish_refcount_update(tp, cudp, -				&dfops, type, refc->pe_startblock, refc->pe_len, +				dfops, type, refc->pe_startblock, refc->pe_len,  				&new_fsb, &new_len, &rcur);  		if (error)  			goto abort_error; @@ -497,21 +495,21 @@ xfs_cui_recover(  			switch (type) {  			case XFS_REFCOUNT_INCREASE:  				error = xfs_refcount_increase_extent( -						tp->t_mountp, &dfops, &irec); +						tp->t_mountp, dfops, &irec);  				break;  			case XFS_REFCOUNT_DECREASE:  				error = xfs_refcount_decrease_extent( -						tp->t_mountp, &dfops, &irec); +						tp->t_mountp, dfops, &irec);  				break;  			case XFS_REFCOUNT_ALLOC_COW:  				error = xfs_refcount_alloc_cow_extent( -						tp->t_mountp, &dfops, +						tp->t_mountp, dfops,  						irec.br_startblock,  						irec.br_blockcount);  				break;  			case XFS_REFCOUNT_FREE_COW:  				error = xfs_refcount_free_cow_extent( -						tp->t_mountp, &dfops, +						tp->t_mountp, dfops,  						irec.br_startblock,  						irec.br_blockcount);  				break; @@ -525,17 +523,12 @@ xfs_cui_recover(  	}  	xfs_refcount_finish_one_cleanup(tp, rcur, error); -	error = xfs_defer_finish(&tp, &dfops); -	if (error) -		goto abort_defer;  	set_bit(XFS_CUI_RECOVERED, &cuip->cui_flags);  	error = xfs_trans_commit(tp);  	return error;  abort_error:  	xfs_refcount_finish_one_cleanup(tp, rcur, error); -abort_defer: -	xfs_defer_cancel(&dfops);  	xfs_trans_cancel(tp);  	return error;  } diff --git a/fs/xfs/xfs_refcount_item.h b/fs/xfs/xfs_refcount_item.h index 5b74dddfa64b..0e5327349a13 100644 --- a/fs/xfs/xfs_refcount_item.h +++ b/fs/xfs/xfs_refcount_item.h @@ -96,6 +96,7 @@ struct xfs_cud_log_item *xfs_cud_init(struct xfs_mount *,  		struct xfs_cui_log_item *);  void xfs_cui_item_free(struct xfs_cui_log_item *);  void xfs_cui_release(struct xfs_cui_log_item *); -int xfs_cui_recover(struct xfs_mount *mp, struct xfs_cui_log_item *cuip); +int xfs_cui_recover(struct xfs_mount *mp, struct xfs_cui_log_item *cuip, +		struct xfs_defer_ops *dfops);  #endif	/* __XFS_REFCOUNT_ITEM_H__ */ diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index cc041a29eb70..47aea2e82c26 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -49,8 +49,6 @@  #include "xfs_alloc.h"  #include "xfs_quota_defs.h"  #include "xfs_quota.h" -#include "xfs_btree.h" -#include "xfs_bmap_btree.h"  #include "xfs_reflink.h"  #include "xfs_iomap.h"  #include "xfs_rmap_btree.h" @@ -456,6 +454,8 @@ retry:  	if (error)  		goto out_bmap_cancel; +	xfs_inode_set_cowblocks_tag(ip); +  	/* Finish up. */  	error = xfs_defer_finish(&tp, &dfops);  	if (error) @@ -492,8 +492,9 @@ xfs_reflink_find_cow_mapping(  	struct xfs_iext_cursor		icur;  	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED)); -	ASSERT(xfs_is_reflink_inode(ip)); +	if (!xfs_is_reflink_inode(ip)) +		return false;  	offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);  	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &icur, &got))  		return false; @@ -612,6 +613,9 @@ xfs_reflink_cancel_cow_blocks(  			/* Remove the mapping from the CoW fork. */  			xfs_bmap_del_extent_cow(ip, &icur, &got, &del); +		} else { +			/* Didn't do anything, push cursor back. */ +			xfs_iext_prev(ifp, &icur);  		}  next_extent:  		if (!xfs_iext_get_extent(ifp, &icur, &got)) @@ -727,7 +731,7 @@ xfs_reflink_end_cow(  			(unsigned int)(end_fsb - offset_fsb),  			XFS_DATA_FORK);  	error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write, -			resblks, 0, 0, &tp); +			resblks, 0, XFS_TRANS_RESERVE, &tp);  	if (error)  		goto out; @@ -1293,6 +1297,17 @@ xfs_reflink_remap_range(  	trace_xfs_reflink_remap_range(src, pos_in, len, dest, pos_out); +	/* +	 * Clear out post-eof preallocations because we don't have page cache +	 * backing the delayed allocations and they'll never get freed on +	 * their own. +	 */ +	if (xfs_can_free_eofblocks(dest, true)) { +		ret = xfs_free_eofblocks(dest); +		if (ret) +			goto out_unlock; +	} +  	/* Set flags and remap blocks. */  	ret = xfs_reflink_set_inode_flag(src, dest);  	if (ret) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index f663022353c0..1dacccc367f8 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -212,9 +212,9 @@ xfs_parseargs(  	 */  	if (sb_rdonly(sb))  		mp->m_flags |= XFS_MOUNT_RDONLY; -	if (sb->s_flags & MS_DIRSYNC) +	if (sb->s_flags & SB_DIRSYNC)  		mp->m_flags |= XFS_MOUNT_DIRSYNC; -	if (sb->s_flags & MS_SYNCHRONOUS) +	if (sb->s_flags & SB_SYNCHRONOUS)  		mp->m_flags |= XFS_MOUNT_WSYNC;  	/* @@ -1312,7 +1312,7 @@ xfs_fs_remount(  	}  	/* ro -> rw */ -	if ((mp->m_flags & XFS_MOUNT_RDONLY) && !(*flags & MS_RDONLY)) { +	if ((mp->m_flags & XFS_MOUNT_RDONLY) && !(*flags & SB_RDONLY)) {  		if (mp->m_flags & XFS_MOUNT_NORECOVERY) {  			xfs_warn(mp,  		"ro->rw transition prohibited on norecovery mount"); @@ -1360,6 +1360,7 @@ xfs_fs_remount(  			xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);  			return error;  		} +		xfs_queue_cowblocks(mp);  		/* Create the per-AG metadata reservation pool .*/  		error = xfs_fs_reserve_ag_blocks(mp); @@ -1368,7 +1369,15 @@ xfs_fs_remount(  	}  	/* rw -> ro */ -	if (!(mp->m_flags & XFS_MOUNT_RDONLY) && (*flags & MS_RDONLY)) { +	if (!(mp->m_flags & XFS_MOUNT_RDONLY) && (*flags & SB_RDONLY)) { +		/* Get rid of any leftover CoW reservations... */ +		cancel_delayed_work_sync(&mp->m_cowblocks_work); +		error = xfs_icache_free_cowblocks(mp, NULL); +		if (error) { +			xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); +			return error; +		} +  		/* Free the per-AG metadata reservation pool. */  		error = xfs_fs_unreserve_ag_blocks(mp);  		if (error) { diff --git a/fs/xfs/xfs_super.h b/fs/xfs/xfs_super.h index 5f2f32408011..fcc5dfc70aa0 100644 --- a/fs/xfs/xfs_super.h +++ b/fs/xfs/xfs_super.h @@ -30,7 +30,7 @@ extern void xfs_qm_exit(void);  #ifdef CONFIG_XFS_POSIX_ACL  # define XFS_ACL_STRING		"ACLs, " -# define set_posix_acl_flag(sb)	((sb)->s_flags |= MS_POSIXACL) +# define set_posix_acl_flag(sb)	((sb)->s_flags |= SB_POSIXACL)  #else  # define XFS_ACL_STRING  # define set_posix_acl_flag(sb)	do { } while (0) diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 68d3ca2c4968..2e9e793a8f9d 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -232,11 +232,6 @@ xfs_symlink(  	resblks = XFS_SYMLINK_SPACE_RES(mp, link_name->len, fs_blocks);  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_symlink, resblks, 0, 0, &tp); -	if (error == -ENOSPC && fs_blocks == 0) { -		resblks = 0; -		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_symlink, 0, 0, 0, -				&tp); -	}  	if (error)  		goto out_release_inode; @@ -260,14 +255,6 @@ xfs_symlink(  		goto out_trans_cancel;  	/* -	 * Check for ability to enter directory entry, if no space reserved. -	 */ -	if (!resblks) { -		error = xfs_dir_canenter(tp, dp, link_name); -		if (error) -			goto out_trans_cancel; -	} -	/*  	 * Initialize the bmap freelist prior to calling either  	 * bmapi or the directory create code.  	 */ @@ -277,7 +264,7 @@ xfs_symlink(  	 * Allocate an inode for the symlink.  	 */  	error = xfs_dir_ialloc(&tp, dp, S_IFLNK | (mode & ~S_IFMT), 1, 0, -			       prid, resblks > 0, &ip, NULL); +			       prid, &ip, NULL);  	if (error)  		goto out_trans_cancel; diff --git a/fs/xfs/xfs_trace.c b/fs/xfs/xfs_trace.c index 5d95fe348294..35f3546b6af5 100644 --- a/fs/xfs/xfs_trace.c +++ b/fs/xfs/xfs_trace.c @@ -24,7 +24,6 @@  #include "xfs_mount.h"  #include "xfs_defer.h"  #include "xfs_da_format.h" -#include "xfs_defer.h"  #include "xfs_inode.h"  #include "xfs_btree.h"  #include "xfs_da_btree.h"  | 

