diff options
Diffstat (limited to 'fs/xfs/xfs_inode.c')
| -rw-r--r-- | fs/xfs/xfs_inode.c | 496 | 
1 files changed, 413 insertions, 83 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index d55b42b2480d..58fb7a5062e1 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -16,6 +16,7 @@  #include "xfs_inode.h"  #include "xfs_dir2.h"  #include "xfs_attr.h" +#include "xfs_bit.h"  #include "xfs_trans_space.h"  #include "xfs_trans.h"  #include "xfs_buf_item.h" @@ -38,13 +39,12 @@  #include "xfs_ag.h"  #include "xfs_log_priv.h"  #include "xfs_health.h" +#include "xfs_pnfs.h" +#include "xfs_parent.h" +#include "xfs_xattr.h"  struct kmem_cache *xfs_inode_cache; -STATIC int xfs_iunlink(struct xfs_trans *, struct xfs_inode *); -STATIC int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag, -	struct xfs_inode *); -  /*   * helper function to extract extent size hint from inode   */ @@ -60,7 +60,8 @@ xfs_get_extsz_hint(  		return 0;  	if ((ip->i_diflags & XFS_DIFLAG_EXTSIZE) && ip->i_extsize)  		return ip->i_extsize; -	if (XFS_IS_REALTIME_INODE(ip)) +	if (XFS_IS_REALTIME_INODE(ip) && +	    ip->i_mount->m_sb.sb_rextsize > 1)  		return ip->i_mount->m_sb.sb_rextsize;  	return 0;  } @@ -420,7 +421,7 @@ xfs_lock_inumorder(   * lock more than one at a time, lockdep will report false positives saying we   * have violated locking orders.   */ -static void +void  xfs_lock_inodes(  	struct xfs_inode	**ips,  	int			inodes, @@ -749,6 +750,8 @@ xfs_inode_inherit_flags2(  /*   * Initialise a newly allocated inode and return the in-core inode to the   * caller locked exclusively. + * + * Caller is responsible for unlocking the inode manually upon return   */  int  xfs_init_new_inode( @@ -875,7 +878,7 @@ xfs_init_new_inode(  	/*  	 * Log the new values stuffed into the inode.  	 */ -	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); +	xfs_trans_ijoin(tp, ip, 0);  	xfs_trans_log_inode(tp, ip, flags);  	/* now that we have an i_mode we can setup the inode structure */ @@ -890,24 +893,27 @@ xfs_init_new_inode(   * link count to go to zero, move the inode to AGI unlinked list so that it can   * be freed when the last active reference goes away via xfs_inactive().   */ -static int			/* error */ +int  xfs_droplink( -	xfs_trans_t *tp, -	xfs_inode_t *ip) +	struct xfs_trans	*tp, +	struct xfs_inode	*ip)  { -	if (VFS_I(ip)->i_nlink == 0) { -		xfs_alert(ip->i_mount, -			  "%s: Attempt to drop inode (%llu) with nlink zero.", -			  __func__, ip->i_ino); -		return -EFSCORRUPTED; -	} +	struct inode		*inode = VFS_I(ip);  	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); -	drop_nlink(VFS_I(ip)); +	if (inode->i_nlink == 0) { +		xfs_info_ratelimited(tp->t_mountp, + "Inode 0x%llx link count dropped below zero.  Pinning link count.", +				ip->i_ino); +		set_nlink(inode, XFS_NLINK_PINNED); +	} +	if (inode->i_nlink != XFS_NLINK_PINNED) +		drop_nlink(inode); +  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); -	if (VFS_I(ip)->i_nlink) +	if (inode->i_nlink)  		return 0;  	return xfs_iunlink(tp, ip); @@ -916,14 +922,22 @@ xfs_droplink(  /*   * Increment the link count on an inode & log the change.   */ -static void +void  xfs_bumplink( -	xfs_trans_t *tp, -	xfs_inode_t *ip) +	struct xfs_trans	*tp, +	struct xfs_inode	*ip)  { +	struct inode		*inode = VFS_I(ip); +  	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); -	inc_nlink(VFS_I(ip)); +	if (inode->i_nlink == XFS_NLINK_PINNED - 1) +		xfs_info_ratelimited(tp->t_mountp, + "Inode 0x%llx link count exceeded maximum.  Pinning link count.", +				ip->i_ino); +	if (inode->i_nlink != XFS_NLINK_PINNED) +		inc_nlink(inode); +  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);  } @@ -1005,7 +1019,7 @@ xfs_dir_hook_setup(  int  xfs_create(  	struct mnt_idmap	*idmap, -	xfs_inode_t		*dp, +	struct xfs_inode	*dp,  	struct xfs_name		*name,  	umode_t			mode,  	dev_t			rdev, @@ -1017,7 +1031,7 @@ xfs_create(  	struct xfs_inode	*ip = NULL;  	struct xfs_trans	*tp = NULL;  	int			error; -	bool                    unlock_dp_on_error = false; +	bool			unlock_dp_on_error = false;  	prid_t			prid;  	struct xfs_dquot	*udqp = NULL;  	struct xfs_dquot	*gdqp = NULL; @@ -1025,6 +1039,7 @@ xfs_create(  	struct xfs_trans_res	*tres;  	uint			resblks;  	xfs_ino_t		ino; +	struct xfs_parent_args	*ppargs;  	trace_xfs_create(dp, name); @@ -1046,13 +1061,17 @@ xfs_create(  		return error;  	if (is_dir) { -		resblks = XFS_MKDIR_SPACE_RES(mp, name->len); +		resblks = xfs_mkdir_space_res(mp, name->len);  		tres = &M_RES(mp)->tr_mkdir;  	} else { -		resblks = XFS_CREATE_SPACE_RES(mp, name->len); +		resblks = xfs_create_space_res(mp, name->len);  		tres = &M_RES(mp)->tr_create;  	} +	error = xfs_parent_start(mp, &ppargs); +	if (error) +		goto out_release_dquots; +  	/*  	 * Initially assume that the file does not exist and  	 * reserve the resources for that case.  If that is not @@ -1068,7 +1087,7 @@ xfs_create(  				resblks, &tp);  	}  	if (error) -		goto out_release_dquots; +		goto out_parent;  	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);  	unlock_dp_on_error = true; @@ -1092,8 +1111,7 @@ xfs_create(  	 * the transaction cancel unlocking dp so don't do it explicitly in the  	 * error path.  	 */ -	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); -	unlock_dp_on_error = false; +	xfs_trans_ijoin(tp, dp, 0);  	error = xfs_dir_createname(tp, dp, name, ip->i_ino,  					resblks - XFS_IALLOC_SPACE_RES(mp)); @@ -1113,6 +1131,16 @@ xfs_create(  	}  	/* +	 * If we have parent pointers, we need to add the attribute containing +	 * the parent information now. +	 */ +	if (ppargs) { +		error = xfs_parent_addname(tp, ppargs, dp, name, ip); +		if (error) +			goto out_trans_cancel; +	} + +	/*  	 * Create ip with a reference from dp, and add '.' and '..' references  	 * if it's a directory.  	 */ @@ -1142,6 +1170,9 @@ xfs_create(  	xfs_qm_dqrele(pdqp);  	*ipp = ip; +	xfs_iunlock(ip, XFS_ILOCK_EXCL); +	xfs_iunlock(dp, XFS_ILOCK_EXCL); +	xfs_parent_finish(mp, ppargs);  	return 0;   out_trans_cancel: @@ -1153,9 +1184,12 @@ xfs_create(  	 * transactions and deadlocks from xfs_inactive.  	 */  	if (ip) { +		xfs_iunlock(ip, XFS_ILOCK_EXCL);  		xfs_finish_inode_setup(ip);  		xfs_irele(ip);  	} + out_parent: +	xfs_parent_finish(mp, ppargs);   out_release_dquots:  	xfs_qm_dqrele(udqp);  	xfs_qm_dqrele(gdqp); @@ -1171,6 +1205,7 @@ xfs_create_tmpfile(  	struct mnt_idmap	*idmap,  	struct xfs_inode	*dp,  	umode_t			mode, +	bool			init_xattrs,  	struct xfs_inode	**ipp)  {  	struct xfs_mount	*mp = dp->i_mount; @@ -1211,7 +1246,7 @@ xfs_create_tmpfile(  	error = xfs_dialloc(&tp, dp->i_ino, mode, &ino);  	if (!error)  		error = xfs_init_new_inode(idmap, tp, dp, ino, mode, -				0, 0, prid, false, &ip); +				0, 0, prid, init_xattrs, &ip);  	if (error)  		goto out_trans_cancel; @@ -1238,6 +1273,7 @@ xfs_create_tmpfile(  	xfs_qm_dqrele(pdqp);  	*ipp = ip; +	xfs_iunlock(ip, XFS_ILOCK_EXCL);  	return 0;   out_trans_cancel: @@ -1249,6 +1285,7 @@ xfs_create_tmpfile(  	 * transactions and deadlocks from xfs_inactive.  	 */  	if (ip) { +		xfs_iunlock(ip, XFS_ILOCK_EXCL);  		xfs_finish_inode_setup(ip);  		xfs_irele(ip);  	} @@ -1262,14 +1299,15 @@ xfs_create_tmpfile(  int  xfs_link( -	xfs_inode_t		*tdp, -	xfs_inode_t		*sip, +	struct xfs_inode	*tdp, +	struct xfs_inode	*sip,  	struct xfs_name		*target_name)  { -	xfs_mount_t		*mp = tdp->i_mount; -	xfs_trans_t		*tp; +	struct xfs_mount	*mp = tdp->i_mount; +	struct xfs_trans	*tp;  	int			error, nospace_error = 0;  	int			resblks; +	struct xfs_parent_args	*ppargs;  	trace_xfs_link(tdp, target_name); @@ -1288,11 +1326,25 @@ xfs_link(  	if (error)  		goto std_return; -	resblks = XFS_LINK_SPACE_RES(mp, target_name->len); +	error = xfs_parent_start(mp, &ppargs); +	if (error) +		goto std_return; + +	resblks = xfs_link_space_res(mp, target_name->len);  	error = xfs_trans_alloc_dir(tdp, &M_RES(mp)->tr_link, sip, &resblks,  			&tp, &nospace_error);  	if (error) -		goto std_return; +		goto out_parent; + +	/* +	 * We don't allow reservationless or quotaless hardlinking when parent +	 * pointers are enabled because we can't back out if the xattrs must +	 * grow. +	 */ +	if (ppargs && nospace_error) { +		error = nospace_error; +		goto error_return; +	}  	/*  	 * If we are using project inheritance, we only allow hard link @@ -1343,6 +1395,19 @@ xfs_link(  	xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE);  	xfs_bumplink(tp, sip); + +	/* +	 * If we have parent pointers, we now need to add the parent record to +	 * the attribute fork of the inode. If this is the initial parent +	 * attribute, we need to create it correctly, otherwise we can just add +	 * the parent to the inode. +	 */ +	if (ppargs) { +		error = xfs_parent_addname(tp, ppargs, tdp, target_name, sip); +		if (error) +			goto error_return; +	} +  	xfs_dir_update_hook(tdp, sip, 1, target_name);  	/* @@ -1353,10 +1418,18 @@ xfs_link(  	if (xfs_has_wsync(mp) || xfs_has_dirsync(mp))  		xfs_trans_set_sync(tp); -	return xfs_trans_commit(tp); +	error = xfs_trans_commit(tp); +	xfs_iunlock(tdp, XFS_ILOCK_EXCL); +	xfs_iunlock(sip, XFS_ILOCK_EXCL); +	xfs_parent_finish(mp, ppargs); +	return error;   error_return:  	xfs_trans_cancel(tp); +	xfs_iunlock(tdp, XFS_ILOCK_EXCL); +	xfs_iunlock(sip, XFS_ILOCK_EXCL); + out_parent: +	xfs_parent_finish(mp, ppargs);   std_return:  	if (error == -ENOSPC && nospace_error)  		error = nospace_error; @@ -1555,6 +1628,51 @@ out_unlock:  }  /* + * Mark all the buffers attached to this directory stale.  In theory we should + * never be freeing a directory with any blocks at all, but this covers the + * case where we've recovered a directory swap with a "temporary" directory + * created by online repair and now need to dump it. + */ +STATIC void +xfs_inactive_dir( +	struct xfs_inode	*dp) +{ +	struct xfs_iext_cursor	icur; +	struct xfs_bmbt_irec	got; +	struct xfs_mount	*mp = dp->i_mount; +	struct xfs_da_geometry	*geo = mp->m_dir_geo; +	struct xfs_ifork	*ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK); +	xfs_fileoff_t		off; + +	/* +	 * Invalidate each directory block.  All directory blocks are of +	 * fsbcount length and alignment, so we only need to walk those same +	 * offsets.  We hold the only reference to this inode, so we must wait +	 * for the buffer locks. +	 */ +	for_each_xfs_iext(ifp, &icur, &got) { +		for (off = round_up(got.br_startoff, geo->fsbcount); +		     off < got.br_startoff + got.br_blockcount; +		     off += geo->fsbcount) { +			struct xfs_buf	*bp = NULL; +			xfs_fsblock_t	fsbno; +			int		error; + +			fsbno = (off - got.br_startoff) + got.br_startblock; +			error = xfs_buf_incore(mp->m_ddev_targp, +					XFS_FSB_TO_DADDR(mp, fsbno), +					XFS_FSB_TO_BB(mp, geo->fsbcount), +					XBF_LIVESCAN, &bp); +			if (error) +				continue; + +			xfs_buf_stale(bp); +			xfs_buf_relse(bp); +		} +	} +} + +/*   * xfs_inactive_truncate   *   * Called to perform a truncate when an inode becomes unlinked. @@ -1864,6 +1982,11 @@ xfs_inactive(  			goto out;  	} +	if (S_ISDIR(VFS_I(ip)->i_mode) && ip->i_df.if_nextents > 0) { +		xfs_inactive_dir(ip); +		truncate = 1; +	} +  	if (S_ISLNK(VFS_I(ip)->i_mode))  		error = xfs_inactive_symlink(ip);  	else if (truncate) @@ -1937,7 +2060,7 @@ out:   * only unlinked, referenced inodes can be on the unlinked inode list.  If we   * don't find the inode in cache, then let the caller handle the situation.   */ -static struct xfs_inode * +struct xfs_inode *  xfs_iunlink_lookup(  	struct xfs_perag	*pag,  	xfs_agino_t		agino) @@ -2150,7 +2273,7 @@ xfs_iunlink_insert_inode(   * We place the on-disk inode on a list in the AGI.  It will be pulled from this   * list when the inode is freed.   */ -STATIC int +int  xfs_iunlink(  	struct xfs_trans	*tp,  	struct xfs_inode	*ip) @@ -2167,7 +2290,7 @@ xfs_iunlink(  	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));  	/* Get the agi buffer first.  It ensures lock ordering on the list. */ -	error = xfs_read_agi(pag, tp, &agibp); +	error = xfs_read_agi(pag, tp, 0, &agibp);  	if (error)  		goto out; @@ -2252,7 +2375,7 @@ xfs_iunlink_remove_inode(  /*   * Pull the on-disk inode from the AGI unlinked list.   */ -STATIC int +int  xfs_iunlink_remove(  	struct xfs_trans	*tp,  	struct xfs_perag	*pag, @@ -2264,7 +2387,7 @@ xfs_iunlink_remove(  	trace_xfs_iunlink_remove(ip);  	/* Get the agi buffer first.  It ensures lock ordering on the list. */ -	error = xfs_read_agi(pag, tp, &agibp); +	error = xfs_read_agi(pag, tp, 0, &agibp);  	if (error)  		return error; @@ -2598,16 +2721,17 @@ xfs_iunpin_wait(   */  int  xfs_remove( -	xfs_inode_t             *dp, +	struct xfs_inode	*dp,  	struct xfs_name		*name, -	xfs_inode_t		*ip) +	struct xfs_inode	*ip)  { -	xfs_mount_t		*mp = dp->i_mount; -	xfs_trans_t             *tp = NULL; +	struct xfs_mount	*mp = dp->i_mount; +	struct xfs_trans	*tp = NULL;  	int			is_dir = S_ISDIR(VFS_I(ip)->i_mode);  	int			dontcare;  	int                     error = 0;  	uint			resblks; +	struct xfs_parent_args	*ppargs;  	trace_xfs_remove(dp, name); @@ -2624,6 +2748,10 @@ xfs_remove(  	if (error)  		goto std_return; +	error = xfs_parent_start(mp, &ppargs); +	if (error) +		goto std_return; +  	/*  	 * We try to get the real space reservation first, allowing for  	 * directory btree deletion(s) implying possible bmap insert(s).  If we @@ -2635,12 +2763,12 @@ xfs_remove(  	 * the directory code can handle a reservationless update and we don't  	 * want to prevent a user from trying to free space by deleting things.  	 */ -	resblks = XFS_REMOVE_SPACE_RES(mp); +	resblks = xfs_remove_space_res(mp, name->len);  	error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip, &resblks,  			&tp, &dontcare);  	if (error) {  		ASSERT(error != -ENOSPC); -		goto std_return; +		goto out_parent;  	}  	/* @@ -2700,6 +2828,13 @@ xfs_remove(  		goto out_trans_cancel;  	} +	/* Remove parent pointer. */ +	if (ppargs) { +		error = xfs_parent_removename(tp, ppargs, dp, name, ip); +		if (error) +			goto out_trans_cancel; +	} +  	/*  	 * Drop the link from dp to ip, and if ip was a directory, remove the  	 * '.' and '..' references since we freed the directory. @@ -2716,19 +2851,42 @@ xfs_remove(  	error = xfs_trans_commit(tp);  	if (error) -		goto std_return; +		goto out_unlock;  	if (is_dir && xfs_inode_is_filestream(ip))  		xfs_filestream_deassociate(ip); +	xfs_iunlock(ip, XFS_ILOCK_EXCL); +	xfs_iunlock(dp, XFS_ILOCK_EXCL); +	xfs_parent_finish(mp, ppargs);  	return 0;   out_trans_cancel:  	xfs_trans_cancel(tp); + out_unlock: +	xfs_iunlock(ip, XFS_ILOCK_EXCL); +	xfs_iunlock(dp, XFS_ILOCK_EXCL); + out_parent: +	xfs_parent_finish(mp, ppargs);   std_return:  	return error;  } +static inline void +xfs_iunlock_rename( +	struct xfs_inode	**i_tab, +	int			num_inodes) +{ +	int			i; + +	for (i = num_inodes - 1; i >= 0; i--) { +		/* Skip duplicate inodes if src and target dps are the same */ +		if (!i_tab[i] || (i > 0 && i_tab[i] == i_tab[i - 1])) +			continue; +		xfs_iunlock(i_tab[i], XFS_ILOCK_EXCL); +	} +} +  /*   * Enter all inodes for a rename transaction into a sorted array.   */ @@ -2743,7 +2901,7 @@ xfs_sort_for_rename(  	struct xfs_inode	**i_tab,/* out: sorted array of inodes */  	int			*num_inodes)  /* in/out: inodes in array */  { -	int			i, j; +	int			i;  	ASSERT(*num_inodes == __XFS_SORT_INODES);  	memset(i_tab, 0, *num_inodes * sizeof(struct xfs_inode *)); @@ -2765,17 +2923,26 @@ xfs_sort_for_rename(  		i_tab[i++] = wip;  	*num_inodes = i; +	xfs_sort_inodes(i_tab, *num_inodes); +} + +void +xfs_sort_inodes( +	struct xfs_inode	**i_tab, +	unsigned int		num_inodes) +{ +	int			i, j; + +	ASSERT(num_inodes <= __XFS_SORT_INODES); +  	/*  	 * Sort the elements via bubble sort.  (Remember, there are at  	 * most 5 elements to sort, so this is adequate.)  	 */ -	for (i = 0; i < *num_inodes; i++) { -		for (j = 1; j < *num_inodes; j++) { -			if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { -				struct xfs_inode *temp = i_tab[j]; -				i_tab[j] = i_tab[j-1]; -				i_tab[j-1] = temp; -			} +	for (i = 0; i < num_inodes; i++) { +		for (j = 1; j < num_inodes; j++) { +			if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) +				swap(i_tab[j], i_tab[j - 1]);  		}  	}  } @@ -2805,15 +2972,17 @@ xfs_cross_rename(  	struct xfs_inode	*dp1,  	struct xfs_name		*name1,  	struct xfs_inode	*ip1, +	struct xfs_parent_args	*ip1_ppargs,  	struct xfs_inode	*dp2,  	struct xfs_name		*name2,  	struct xfs_inode	*ip2, +	struct xfs_parent_args	*ip2_ppargs,  	int			spaceres)  { -	int		error = 0; -	int		ip1_flags = 0; -	int		ip2_flags = 0; -	int		dp2_flags = 0; +	int			error = 0; +	int			ip1_flags = 0; +	int			ip2_flags = 0; +	int			dp2_flags = 0;  	/* Swap inode number for dirent in first parent */  	error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres); @@ -2882,6 +3051,21 @@ xfs_cross_rename(  		}  	} +	/* Schedule parent pointer replacements */ +	if (ip1_ppargs) { +		error = xfs_parent_replacename(tp, ip1_ppargs, dp1, name1, dp2, +				name2, ip1); +		if (error) +			goto out_trans_abort; +	} + +	if (ip2_ppargs) { +		error = xfs_parent_replacename(tp, ip2_ppargs, dp2, name2, dp1, +				name1, ip2); +		if (error) +			goto out_trans_abort; +	} +  	if (ip1_flags) {  		xfs_trans_ichgtime(tp, ip1, ip1_flags);  		xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE); @@ -2937,7 +3121,7 @@ xfs_rename_alloc_whiteout(  	int			error;  	error = xfs_create_tmpfile(idmap, dp, S_IFCHR | WHITEOUT_MODE, -				   &tmpfile); +			xfs_has_parent(dp->i_mount), &tmpfile);  	if (error)  		return error; @@ -2981,6 +3165,9 @@ xfs_rename(  	struct xfs_trans	*tp;  	struct xfs_inode	*wip = NULL;		/* whiteout inode */  	struct xfs_inode	*inodes[__XFS_SORT_INODES]; +	struct xfs_parent_args	*src_ppargs = NULL; +	struct xfs_parent_args	*tgt_ppargs = NULL; +	struct xfs_parent_args	*wip_ppargs = NULL;  	int			i;  	int			num_inodes = __XFS_SORT_INODES;  	bool			new_parent = (src_dp != target_dp); @@ -3012,9 +3199,26 @@ xfs_rename(  	xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,  				inodes, &num_inodes); +	error = xfs_parent_start(mp, &src_ppargs); +	if (error) +		goto out_release_wip; + +	if (wip) { +		error = xfs_parent_start(mp, &wip_ppargs); +		if (error) +			goto out_src_ppargs; +	} + +	if (target_ip) { +		error = xfs_parent_start(mp, &tgt_ppargs); +		if (error) +			goto out_wip_ppargs; +	} +  retry:  	nospace_error = 0; -	spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len); +	spaceres = xfs_rename_space_res(mp, src_name->len, target_ip != NULL, +			target_name->len, wip != NULL);  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, spaceres, 0, 0, &tp);  	if (error == -ENOSPC) {  		nospace_error = error; @@ -3023,14 +3227,26 @@ retry:  				&tp);  	}  	if (error) -		goto out_release_wip; +		goto out_tgt_ppargs; + +	/* +	 * We don't allow reservationless renaming when parent pointers are +	 * enabled because we can't back out if the xattrs must grow. +	 */ +	if (src_ppargs && nospace_error) { +		error = nospace_error; +		xfs_trans_cancel(tp); +		goto out_tgt_ppargs; +	}  	/*  	 * Attach the dquots to the inodes  	 */  	error = xfs_qm_vop_rename_dqattach(inodes); -	if (error) -		goto out_trans_cancel; +	if (error) { +		xfs_trans_cancel(tp); +		goto out_tgt_ppargs; +	}  	/*  	 * Lock all the participating inodes. Depending upon whether @@ -3041,18 +3257,16 @@ retry:  	xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL);  	/* -	 * Join all the inodes to the transaction. From this point on, -	 * we can rely on either trans_commit or trans_cancel to unlock -	 * them. +	 * Join all the inodes to the transaction.  	 */ -	xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL); +	xfs_trans_ijoin(tp, src_dp, 0);  	if (new_parent) -		xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL); -	xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL); +		xfs_trans_ijoin(tp, target_dp, 0); +	xfs_trans_ijoin(tp, src_ip, 0);  	if (target_ip) -		xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL); +		xfs_trans_ijoin(tp, target_ip, 0);  	if (wip) -		xfs_trans_ijoin(tp, wip, XFS_ILOCK_EXCL); +		xfs_trans_ijoin(tp, wip, 0);  	/*  	 * If we are using project inheritance, we only allow renames @@ -3066,10 +3280,13 @@ retry:  	}  	/* RENAME_EXCHANGE is unique from here on. */ -	if (flags & RENAME_EXCHANGE) -		return xfs_cross_rename(tp, src_dp, src_name, src_ip, -					target_dp, target_name, target_ip, -					spaceres); +	if (flags & RENAME_EXCHANGE) { +		error = xfs_cross_rename(tp, src_dp, src_name, src_ip, +				src_ppargs, target_dp, target_name, target_ip, +				tgt_ppargs, spaceres); +		nospace_error = 0; +		goto out_unlock; +	}  	/*  	 * Try to reserve quota to handle an expansion of the target directory. @@ -3083,6 +3300,7 @@ retry:  		if (error == -EDQUOT || error == -ENOSPC) {  			if (!retried) {  				xfs_trans_cancel(tp); +				xfs_iunlock_rename(inodes, num_inodes);  				xfs_blockgc_free_quota(target_dp, 0);  				retried = true;  				goto retry; @@ -3097,6 +3315,15 @@ retry:  	}  	/* +	 * We don't allow quotaless renaming when parent pointers are enabled +	 * because we can't back out if the xattrs must grow. +	 */ +	if (src_ppargs && nospace_error) { +		error = nospace_error; +		goto out_trans_cancel; +	} + +	/*  	 * Check for expected errors before we dirty the transaction  	 * so we can return an error without a transaction abort.  	 */ @@ -3142,7 +3369,7 @@ retry:  			pag = xfs_perag_get(mp,  					XFS_INO_TO_AGNO(mp, inodes[i]->i_ino)); -			error = xfs_read_agi(pag, tp, &bp); +			error = xfs_read_agi(pag, tp, 0, &bp);  			xfs_perag_put(pag);  			if (error)  				goto out_trans_cancel; @@ -3288,6 +3515,28 @@ retry:  	if (error)  		goto out_trans_cancel; +	/* Schedule parent pointer updates. */ +	if (wip_ppargs) { +		error = xfs_parent_addname(tp, wip_ppargs, src_dp, src_name, +				wip); +		if (error) +			goto out_trans_cancel; +	} + +	if (src_ppargs) { +		error = xfs_parent_replacename(tp, src_ppargs, src_dp, +				src_name, target_dp, target_name, src_ip); +		if (error) +			goto out_trans_cancel; +	} + +	if (tgt_ppargs) { +		error = xfs_parent_removename(tp, tgt_ppargs, target_dp, +				target_name, target_ip); +		if (error) +			goto out_trans_cancel; +	} +  	xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);  	xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);  	if (new_parent) @@ -3309,12 +3558,19 @@ retry:  		xfs_dir_update_hook(src_dp, wip, 1, src_name);  	error = xfs_finish_rename(tp); -	if (wip) -		xfs_irele(wip); -	return error; +	nospace_error = 0; +	goto out_unlock;  out_trans_cancel:  	xfs_trans_cancel(tp); +out_unlock: +	xfs_iunlock_rename(inodes, num_inodes); +out_tgt_ppargs: +	xfs_parent_finish(mp, tgt_ppargs); +out_wip_ppargs: +	xfs_parent_finish(mp, wip_ppargs); +out_src_ppargs: +	xfs_parent_finish(mp, src_ppargs);  out_release_wip:  	if (wip)  		xfs_irele(wip); @@ -3814,7 +4070,7 @@ xfs_inode_reload_unlinked_bucket(  	/* Grab the first inode in the list */  	pag = xfs_perag_get(mp, agno); -	error = xfs_ialloc_read_agi(pag, tp, &agibp); +	error = xfs_ialloc_read_agi(pag, tp, 0, &agibp);  	xfs_perag_put(pag);  	if (error)  		return error; @@ -3946,3 +4202,77 @@ xfs_inode_count_blocks(  		xfs_bmap_count_leaves(ifp, rblocks);  	*dblocks = ip->i_nblocks - *rblocks;  } + +static void +xfs_wait_dax_page( +	struct inode		*inode) +{ +	struct xfs_inode        *ip = XFS_I(inode); + +	xfs_iunlock(ip, XFS_MMAPLOCK_EXCL); +	schedule(); +	xfs_ilock(ip, XFS_MMAPLOCK_EXCL); +} + +int +xfs_break_dax_layouts( +	struct inode		*inode, +	bool			*retry) +{ +	struct page		*page; + +	xfs_assert_ilocked(XFS_I(inode), XFS_MMAPLOCK_EXCL); + +	page = dax_layout_busy_page(inode->i_mapping); +	if (!page) +		return 0; + +	*retry = true; +	return ___wait_var_event(&page->_refcount, +			atomic_read(&page->_refcount) == 1, TASK_INTERRUPTIBLE, +			0, 0, xfs_wait_dax_page(inode)); +} + +int +xfs_break_layouts( +	struct inode		*inode, +	uint			*iolock, +	enum layout_break_reason reason) +{ +	bool			retry; +	int			error; + +	xfs_assert_ilocked(XFS_I(inode), XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL); + +	do { +		retry = false; +		switch (reason) { +		case BREAK_UNMAP: +			error = xfs_break_dax_layouts(inode, &retry); +			if (error || retry) +				break; +			fallthrough; +		case BREAK_WRITE: +			error = xfs_break_leased_layouts(inode, iolock, &retry); +			break; +		default: +			WARN_ON_ONCE(1); +			error = -EINVAL; +		} +	} while (error == 0 && retry); + +	return error; +} + +/* Returns the size of fundamental allocation unit for a file, in bytes. */ +unsigned int +xfs_inode_alloc_unitsize( +	struct xfs_inode	*ip) +{ +	unsigned int		blocks = 1; + +	if (XFS_IS_REALTIME_INODE(ip)) +		blocks = ip->i_mount->m_sb.sb_rextsize; + +	return XFS_FSB_TO_B(ip->i_mount, blocks); +}  |