diff options
Diffstat (limited to 'fs/xfs/xfs_inode.c')
| -rw-r--r-- | fs/xfs/xfs_inode.c | 141 | 
1 files changed, 98 insertions, 43 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 3da9f4da4f3d..dc40a6d5ae0d 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -164,7 +164,7 @@ xfs_ilock(  	       (XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));  	ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=  	       (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)); -	ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_DEP_MASK)) == 0); +	ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_SUBCLASS_MASK)) == 0);  	if (lock_flags & XFS_IOLOCK_EXCL)  		mrupdate_nested(&ip->i_iolock, XFS_IOLOCK_DEP(lock_flags)); @@ -212,7 +212,7 @@ xfs_ilock_nowait(  	       (XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));  	ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=  	       (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)); -	ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_DEP_MASK)) == 0); +	ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_SUBCLASS_MASK)) == 0);  	if (lock_flags & XFS_IOLOCK_EXCL) {  		if (!mrtryupdate(&ip->i_iolock)) @@ -281,7 +281,7 @@ xfs_iunlock(  	       (XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));  	ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=  	       (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)); -	ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_DEP_MASK)) == 0); +	ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_SUBCLASS_MASK)) == 0);  	ASSERT(lock_flags != 0);  	if (lock_flags & XFS_IOLOCK_EXCL) @@ -363,31 +363,57 @@ int xfs_lock_delays;  #endif  /* + * xfs_lockdep_subclass_ok() is only used in an ASSERT, so is only called when + * DEBUG or XFS_WARN is set. And MAX_LOCKDEP_SUBCLASSES is then only defined + * when CONFIG_LOCKDEP is set. Hence the complex define below to avoid build + * errors and warnings. + */ +#if (defined(DEBUG) || defined(XFS_WARN)) && defined(CONFIG_LOCKDEP) +static bool +xfs_lockdep_subclass_ok( +	int subclass) +{ +	return subclass < MAX_LOCKDEP_SUBCLASSES; +} +#else +#define xfs_lockdep_subclass_ok(subclass)	(true) +#endif + +/*   * Bump the subclass so xfs_lock_inodes() acquires each lock with a different - * value. This shouldn't be called for page fault locking, but we also need to - * ensure we don't overrun the number of lockdep subclasses for the iolock or - * mmaplock as that is limited to 12 by the mmap lock lockdep annotations. + * value. This can be called for any type of inode lock combination, including + * parent locking. Care must be taken to ensure we don't overrun the subclass + * storage fields in the class mask we build.   */  static inline int  xfs_lock_inumorder(int lock_mode, int subclass)  { +	int	class = 0; + +	ASSERT(!(lock_mode & (XFS_ILOCK_PARENT | XFS_ILOCK_RTBITMAP | +			      XFS_ILOCK_RTSUM))); +	ASSERT(xfs_lockdep_subclass_ok(subclass)); +  	if (lock_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL)) { -		ASSERT(subclass + XFS_LOCK_INUMORDER < -			(1 << (XFS_MMAPLOCK_SHIFT - XFS_IOLOCK_SHIFT))); -		lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_IOLOCK_SHIFT; +		ASSERT(subclass <= XFS_IOLOCK_MAX_SUBCLASS); +		ASSERT(xfs_lockdep_subclass_ok(subclass + +						XFS_IOLOCK_PARENT_VAL)); +		class += subclass << XFS_IOLOCK_SHIFT; +		if (lock_mode & XFS_IOLOCK_PARENT) +			class += XFS_IOLOCK_PARENT_VAL << XFS_IOLOCK_SHIFT;  	}  	if (lock_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) { -		ASSERT(subclass + XFS_LOCK_INUMORDER < -			(1 << (XFS_ILOCK_SHIFT - XFS_MMAPLOCK_SHIFT))); -		lock_mode |= (subclass + XFS_LOCK_INUMORDER) << -							XFS_MMAPLOCK_SHIFT; +		ASSERT(subclass <= XFS_MMAPLOCK_MAX_SUBCLASS); +		class += subclass << XFS_MMAPLOCK_SHIFT;  	} -	if (lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)) -		lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_ILOCK_SHIFT; +	if (lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)) { +		ASSERT(subclass <= XFS_ILOCK_MAX_SUBCLASS); +		class += subclass << XFS_ILOCK_SHIFT; +	} -	return lock_mode; +	return (lock_mode & ~XFS_LOCK_SUBCLASS_MASK) | class;  }  /* @@ -399,6 +425,11 @@ xfs_lock_inumorder(int lock_mode, int subclass)   * transaction (such as truncate). This can result in deadlock since the long   * running trans might need to wait for the inode we just locked in order to   * push the tail and free space in the log. + * + * xfs_lock_inodes() can only be used to lock one type of lock at a time - + * the iolock, the mmaplock or the ilock, but not more than one at a time. If we + * lock more than one at a time, lockdep will report false positives saying we + * have violated locking orders.   */  void  xfs_lock_inodes( @@ -409,8 +440,29 @@ xfs_lock_inodes(  	int		attempts = 0, i, j, try_lock;  	xfs_log_item_t	*lp; -	/* currently supports between 2 and 5 inodes */ +	/* +	 * Currently supports between 2 and 5 inodes with exclusive locking.  We +	 * support an arbitrary depth of locking here, but absolute limits on +	 * inodes depend on the the type of locking and the limits placed by +	 * lockdep annotations in xfs_lock_inumorder.  These are all checked by +	 * the asserts. +	 */  	ASSERT(ips && inodes >= 2 && inodes <= 5); +	ASSERT(lock_mode & (XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL | +			    XFS_ILOCK_EXCL)); +	ASSERT(!(lock_mode & (XFS_IOLOCK_SHARED | XFS_MMAPLOCK_SHARED | +			      XFS_ILOCK_SHARED))); +	ASSERT(!(lock_mode & XFS_IOLOCK_EXCL) || +		inodes <= XFS_IOLOCK_MAX_SUBCLASS + 1); +	ASSERT(!(lock_mode & XFS_MMAPLOCK_EXCL) || +		inodes <= XFS_MMAPLOCK_MAX_SUBCLASS + 1); +	ASSERT(!(lock_mode & XFS_ILOCK_EXCL) || +		inodes <= XFS_ILOCK_MAX_SUBCLASS + 1); + +	if (lock_mode & XFS_IOLOCK_EXCL) { +		ASSERT(!(lock_mode & (XFS_MMAPLOCK_EXCL | XFS_ILOCK_EXCL))); +	} else if (lock_mode & XFS_MMAPLOCK_EXCL) +		ASSERT(!(lock_mode & XFS_ILOCK_EXCL));  	try_lock = 0;  	i = 0; @@ -629,30 +681,29 @@ xfs_lookup(  {  	xfs_ino_t		inum;  	int			error; -	uint			lock_mode;  	trace_xfs_lookup(dp, name);  	if (XFS_FORCED_SHUTDOWN(dp->i_mount))  		return -EIO; -	lock_mode = xfs_ilock_data_map_shared(dp); +	xfs_ilock(dp, XFS_IOLOCK_SHARED);  	error = xfs_dir_lookup(NULL, dp, name, &inum, ci_name); -	xfs_iunlock(dp, lock_mode); -  	if (error) -		goto out; +		goto out_unlock;  	error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp);  	if (error)  		goto out_free_name; +	xfs_iunlock(dp, XFS_IOLOCK_SHARED);  	return 0;  out_free_name:  	if (ci_name)  		kmem_free(ci_name->name); -out: +out_unlock: +	xfs_iunlock(dp, XFS_IOLOCK_SHARED);  	*ipp = NULL;  	return error;  } @@ -787,7 +838,7 @@ xfs_ialloc(  	if (ip->i_d.di_version == 3) {  		ASSERT(ip->i_d.di_ino == ino); -		ASSERT(uuid_equal(&ip->i_d.di_uuid, &mp->m_sb.sb_uuid)); +		ASSERT(uuid_equal(&ip->i_d.di_uuid, &mp->m_sb.sb_meta_uuid));  		ip->i_d.di_crc = 0;  		ip->i_d.di_changecount = 1;  		ip->i_d.di_lsn = 0; @@ -1149,7 +1200,8 @@ xfs_create(  		goto out_trans_cancel; -	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT); +	xfs_ilock(dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL | +		      XFS_IOLOCK_PARENT | XFS_ILOCK_PARENT);  	unlock_dp_on_error = true;  	xfs_bmap_init(&free_list, &first_block); @@ -1175,11 +1227,8 @@ xfs_create(  	 */  	error = xfs_dir_ialloc(&tp, dp, mode, is_dir ? 2 : 1, rdev,  			       prid, resblks > 0, &ip, &committed); -	if (error) { -		if (error == -ENOSPC) -			goto out_trans_cancel; +	if (error)  		goto out_trans_cancel; -	}  	/*  	 * Now we join the directory inode to the transaction.  We do not do it @@ -1188,7 +1237,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); +	xfs_trans_ijoin(tp, dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);  	unlock_dp_on_error = false;  	error = xfs_dir_createname(tp, dp, name, ip->i_ino, @@ -1261,7 +1310,7 @@ xfs_create(  	xfs_qm_dqrele(pdqp);  	if (unlock_dp_on_error) -		xfs_iunlock(dp, XFS_ILOCK_EXCL); +		xfs_iunlock(dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);  	return error;  } @@ -1318,11 +1367,8 @@ xfs_create_tmpfile(  	error = xfs_dir_ialloc(&tp, dp, mode, 1, 0,  				prid, resblks > 0, &ip, NULL); -	if (error) { -		if (error == -ENOSPC) -			goto out_trans_cancel; +	if (error)  		goto out_trans_cancel; -	}  	if (mp->m_flags & XFS_MOUNT_WSYNC)  		xfs_trans_set_sync(tp); @@ -1409,10 +1455,11 @@ xfs_link(  	if (error)  		goto error_return; +	xfs_ilock(tdp, XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT);  	xfs_lock_two_inodes(sip, tdp, XFS_ILOCK_EXCL);  	xfs_trans_ijoin(tp, sip, XFS_ILOCK_EXCL); -	xfs_trans_ijoin(tp, tdp, XFS_ILOCK_EXCL); +	xfs_trans_ijoin(tp, tdp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);  	/*  	 * If we are using project inheritance, we only allow hard link @@ -1791,14 +1838,15 @@ xfs_inactive_ifree(  	xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_ICOUNT, -1);  	/* -	 * Just ignore errors at this point.  There is nothing we can -	 * do except to try to keep going. Make sure it's not a silent -	 * error. +	 * Just ignore errors at this point.  There is nothing we can do except +	 * to try to keep going. Make sure it's not a silent error.  	 */  	error = xfs_bmap_finish(&tp,  &free_list, &committed); -	if (error) +	if (error) {  		xfs_notice(mp, "%s: xfs_bmap_finish returned error %d",  			__func__, error); +		xfs_bmap_cancel(&free_list); +	}  	error = xfs_trans_commit(tp);  	if (error)  		xfs_notice(mp, "%s: xfs_trans_commit returned error %d", @@ -2515,9 +2563,10 @@ xfs_remove(  		goto out_trans_cancel;  	} +	xfs_ilock(dp, XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT);  	xfs_lock_two_inodes(dp, ip, XFS_ILOCK_EXCL); -	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); +	xfs_trans_ijoin(tp, dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);  	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);  	/* @@ -2898,6 +2947,12 @@ xfs_rename(  	 * whether the target directory is the same as the source  	 * directory, we can lock from 2 to 4 inodes.  	 */ +	if (!new_parent) +		xfs_ilock(src_dp, XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT); +	else +		xfs_lock_two_inodes(src_dp, target_dp, +				    XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT); +  	xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL);  	/* @@ -2905,9 +2960,9 @@ xfs_rename(  	 * we can rely on either trans_commit or trans_cancel to unlock  	 * them.  	 */ -	xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL); +	xfs_trans_ijoin(tp, src_dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);  	if (new_parent) -		xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL); +		xfs_trans_ijoin(tp, target_dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);  	xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL);  	if (target_ip)  		xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL);  |