diff options
Diffstat (limited to '')
| -rw-r--r-- | fs/xfs/xfs_icache.c | 148 | 
1 files changed, 120 insertions, 28 deletions
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index c48df5f25b9f..981b2cf51985 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -33,6 +33,9 @@  #include "xfs_trace.h"  #include "xfs_icache.h"  #include "xfs_bmap_util.h" +#include "xfs_quota.h" +#include "xfs_dquot_item.h" +#include "xfs_dquot.h"  #include <linux/kthread.h>  #include <linux/freezer.h> @@ -158,7 +161,7 @@ xfs_iget_cache_hit(  	if (ip->i_ino != ino) {  		trace_xfs_iget_skip(ip);  		XFS_STATS_INC(xs_ig_frecycle); -		error = EAGAIN; +		error = -EAGAIN;  		goto out_error;  	} @@ -176,7 +179,7 @@ xfs_iget_cache_hit(  	if (ip->i_flags & (XFS_INEW|XFS_IRECLAIM)) {  		trace_xfs_iget_skip(ip);  		XFS_STATS_INC(xs_ig_frecycle); -		error = EAGAIN; +		error = -EAGAIN;  		goto out_error;  	} @@ -184,7 +187,7 @@ xfs_iget_cache_hit(  	 * If lookup is racing with unlink return an error immediately.  	 */  	if (ip->i_d.di_mode == 0 && !(flags & XFS_IGET_CREATE)) { -		error = ENOENT; +		error = -ENOENT;  		goto out_error;  	} @@ -206,7 +209,7 @@ xfs_iget_cache_hit(  		spin_unlock(&ip->i_flags_lock);  		rcu_read_unlock(); -		error = -inode_init_always(mp->m_super, inode); +		error = inode_init_always(mp->m_super, inode);  		if (error) {  			/*  			 * Re-initializing the inode failed, and we are in deep @@ -243,7 +246,7 @@ xfs_iget_cache_hit(  		/* If the VFS inode is being torn down, pause and try again. */  		if (!igrab(inode)) {  			trace_xfs_iget_skip(ip); -			error = EAGAIN; +			error = -EAGAIN;  			goto out_error;  		} @@ -285,7 +288,7 @@ xfs_iget_cache_miss(  	ip = xfs_inode_alloc(mp, ino);  	if (!ip) -		return ENOMEM; +		return -ENOMEM;  	error = xfs_iread(mp, tp, ip, flags);  	if (error) @@ -294,7 +297,7 @@ xfs_iget_cache_miss(  	trace_xfs_iget_miss(ip);  	if ((ip->i_d.di_mode == 0) && !(flags & XFS_IGET_CREATE)) { -		error = ENOENT; +		error = -ENOENT;  		goto out_destroy;  	} @@ -305,7 +308,7 @@ xfs_iget_cache_miss(  	 * recurse into the file system.  	 */  	if (radix_tree_preload(GFP_NOFS)) { -		error = EAGAIN; +		error = -EAGAIN;  		goto out_destroy;  	} @@ -341,7 +344,7 @@ xfs_iget_cache_miss(  	if (unlikely(error)) {  		WARN_ON(error != -EEXIST);  		XFS_STATS_INC(xs_ig_dup); -		error = EAGAIN; +		error = -EAGAIN;  		goto out_preload_end;  	}  	spin_unlock(&pag->pag_ici_lock); @@ -408,7 +411,7 @@ xfs_iget(  	/* reject inode numbers outside existing AGs */  	if (!ino || XFS_INO_TO_AGNO(mp, ino) >= mp->m_sb.sb_agcount) -		return EINVAL; +		return -EINVAL;  	/* get the perag structure and ensure that it's inode capable */  	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ino)); @@ -445,7 +448,7 @@ again:  	return 0;  out_error_or_again: -	if (error == EAGAIN) { +	if (error == -EAGAIN) {  		delay(1);  		goto again;  	} @@ -489,18 +492,18 @@ xfs_inode_ag_walk_grab(  	/* nothing to sync during shutdown */  	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) -		return EFSCORRUPTED; +		return -EFSCORRUPTED;  	/* If we can't grab the inode, it must on it's way to reclaim. */  	if (!igrab(inode)) -		return ENOENT; +		return -ENOENT;  	/* inode is valid */  	return 0;  out_unlock_noent:  	spin_unlock(&ip->i_flags_lock); -	return ENOENT; +	return -ENOENT;  }  STATIC int @@ -583,16 +586,16 @@ restart:  				continue;  			error = execute(batch[i], flags, args);  			IRELE(batch[i]); -			if (error == EAGAIN) { +			if (error == -EAGAIN) {  				skipped++;  				continue;  			} -			if (error && last_error != EFSCORRUPTED) +			if (error && last_error != -EFSCORRUPTED)  				last_error = error;  		}  		/* bail out if the filesystem is corrupted.  */ -		if (error == EFSCORRUPTED) +		if (error == -EFSCORRUPTED)  			break;  		cond_resched(); @@ -652,11 +655,11 @@ xfs_inode_ag_iterator(  		xfs_perag_put(pag);  		if (error) {  			last_error = error; -			if (error == EFSCORRUPTED) +			if (error == -EFSCORRUPTED)  				break;  		}  	} -	return XFS_ERROR(last_error); +	return last_error;  }  int @@ -680,11 +683,11 @@ xfs_inode_ag_iterator_tag(  		xfs_perag_put(pag);  		if (error) {  			last_error = error; -			if (error == EFSCORRUPTED) +			if (error == -EFSCORRUPTED)  				break;  		}  	} -	return XFS_ERROR(last_error); +	return last_error;  }  /* @@ -944,7 +947,7 @@ restart:  	 * see the stale flag set on the inode.  	 */  	error = xfs_iflush(ip, &bp); -	if (error == EAGAIN) { +	if (error == -EAGAIN) {  		xfs_iunlock(ip, XFS_ILOCK_EXCL);  		/* backoff longer than in xfs_ifree_cluster */  		delay(2); @@ -997,7 +1000,7 @@ out:  	xfs_iflags_clear(ip, XFS_IRECLAIM);  	xfs_iunlock(ip, XFS_ILOCK_EXCL);  	/* -	 * We could return EAGAIN here to make reclaim rescan the inode tree in +	 * We could return -EAGAIN here to make reclaim rescan the inode tree in  	 * a short while. However, this just burns CPU time scanning the tree  	 * waiting for IO to complete and the reclaim work never goes back to  	 * the idle state. Instead, return 0 to let the next scheduled @@ -1100,7 +1103,7 @@ restart:  				if (!batch[i])  					continue;  				error = xfs_reclaim_inode(batch[i], pag, flags); -				if (error && last_error != EFSCORRUPTED) +				if (error && last_error != -EFSCORRUPTED)  					last_error = error;  			} @@ -1129,7 +1132,7 @@ restart:  		trylock = 0;  		goto restart;  	} -	return XFS_ERROR(last_error); +	return last_error;  }  int @@ -1203,6 +1206,30 @@ xfs_inode_match_id(  	return 1;  } +/* + * A union-based inode filtering algorithm. Process the inode if any of the + * criteria match. This is for global/internal scans only. + */ +STATIC int +xfs_inode_match_id_union( +	struct xfs_inode	*ip, +	struct xfs_eofblocks	*eofb) +{ +	if ((eofb->eof_flags & XFS_EOF_FLAGS_UID) && +	    uid_eq(VFS_I(ip)->i_uid, eofb->eof_uid)) +		return 1; + +	if ((eofb->eof_flags & XFS_EOF_FLAGS_GID) && +	    gid_eq(VFS_I(ip)->i_gid, eofb->eof_gid)) +		return 1; + +	if ((eofb->eof_flags & XFS_EOF_FLAGS_PRID) && +	    xfs_get_projid(ip) == eofb->eof_prid) +		return 1; + +	return 0; +} +  STATIC int  xfs_inode_free_eofblocks(  	struct xfs_inode	*ip, @@ -1211,6 +1238,10 @@ xfs_inode_free_eofblocks(  {  	int ret;  	struct xfs_eofblocks *eofb = args; +	bool need_iolock = true; +	int match; + +	ASSERT(!eofb || (eofb && eofb->eof_scan_owner != 0));  	if (!xfs_can_free_eofblocks(ip, false)) {  		/* inode could be preallocated or append-only */ @@ -1228,19 +1259,31 @@ xfs_inode_free_eofblocks(  		return 0;  	if (eofb) { -		if (!xfs_inode_match_id(ip, eofb)) +		if (eofb->eof_flags & XFS_EOF_FLAGS_UNION) +			match = xfs_inode_match_id_union(ip, eofb); +		else +			match = xfs_inode_match_id(ip, eofb); +		if (!match)  			return 0;  		/* skip the inode if the file size is too small */  		if (eofb->eof_flags & XFS_EOF_FLAGS_MINFILESIZE &&  		    XFS_ISIZE(ip) < eofb->eof_min_file_size)  			return 0; + +		/* +		 * A scan owner implies we already hold the iolock. Skip it in +		 * xfs_free_eofblocks() to avoid deadlock. This also eliminates +		 * the possibility of EAGAIN being returned. +		 */ +		if (eofb->eof_scan_owner == ip->i_ino) +			need_iolock = false;  	} -	ret = xfs_free_eofblocks(ip->i_mount, ip, true); +	ret = xfs_free_eofblocks(ip->i_mount, ip, need_iolock);  	/* don't revisit the inode if we're not waiting */ -	if (ret == EAGAIN && !(flags & SYNC_WAIT)) +	if (ret == -EAGAIN && !(flags & SYNC_WAIT))  		ret = 0;  	return ret; @@ -1260,6 +1303,55 @@ xfs_icache_free_eofblocks(  					 eofb, XFS_ICI_EOFBLOCKS_TAG);  } +/* + * Run eofblocks scans on the quotas applicable to the inode. For inodes with + * multiple quotas, we don't know exactly which quota caused an allocation + * failure. We make a best effort by including each quota under low free space + * conditions (less than 1% free space) in the scan. + */ +int +xfs_inode_free_quota_eofblocks( +	struct xfs_inode *ip) +{ +	int scan = 0; +	struct xfs_eofblocks eofb = {0}; +	struct xfs_dquot *dq; + +	ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); + +	/* +	 * Set the scan owner to avoid a potential livelock. Otherwise, the scan +	 * can repeatedly trylock on the inode we're currently processing. We +	 * run a sync scan to increase effectiveness and use the union filter to +	 * cover all applicable quotas in a single scan. +	 */ +	eofb.eof_scan_owner = ip->i_ino; +	eofb.eof_flags = XFS_EOF_FLAGS_UNION|XFS_EOF_FLAGS_SYNC; + +	if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) { +		dq = xfs_inode_dquot(ip, XFS_DQ_USER); +		if (dq && xfs_dquot_lowsp(dq)) { +			eofb.eof_uid = VFS_I(ip)->i_uid; +			eofb.eof_flags |= XFS_EOF_FLAGS_UID; +			scan = 1; +		} +	} + +	if (XFS_IS_GQUOTA_ENFORCED(ip->i_mount)) { +		dq = xfs_inode_dquot(ip, XFS_DQ_GROUP); +		if (dq && xfs_dquot_lowsp(dq)) { +			eofb.eof_gid = VFS_I(ip)->i_gid; +			eofb.eof_flags |= XFS_EOF_FLAGS_GID; +			scan = 1; +		} +	} + +	if (scan) +		xfs_icache_free_eofblocks(ip->i_mount, &eofb); + +	return scan; +} +  void  xfs_inode_set_eofblocks_tag(  	xfs_inode_t	*ip)  | 
