Merge branch 'xfs-quota-eofblocks-scan' into for-next
This commit is contained in:
		
						commit
						e0ac6d45bc
					
				| @ -139,6 +139,21 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Check whether a dquot is under low free space conditions. We assume the quota | ||||
|  * is enabled and enforced. | ||||
|  */ | ||||
| static inline bool xfs_dquot_lowsp(struct xfs_dquot *dqp) | ||||
| { | ||||
| 	int64_t freesp; | ||||
| 
 | ||||
| 	freesp = be64_to_cpu(dqp->q_core.d_blk_hardlimit) - dqp->q_res_bcount; | ||||
| 	if (freesp < dqp->q_low_space[XFS_QLOWSP_1_PCNT]) | ||||
| 		return true; | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| #define XFS_DQ_IS_LOCKED(dqp)	(mutex_is_locked(&((dqp)->q_qlock))) | ||||
| #define XFS_DQ_IS_DIRTY(dqp)	((dqp)->dq_flags & XFS_DQ_DIRTY) | ||||
| #define XFS_QM_ISUDQ(dqp)	((dqp)->dq_flags & XFS_DQ_USER) | ||||
|  | ||||
| @ -38,6 +38,7 @@ | ||||
| #include "xfs_trace.h" | ||||
| #include "xfs_log.h" | ||||
| #include "xfs_dinode.h" | ||||
| #include "xfs_icache.h" | ||||
| 
 | ||||
| #include <linux/aio.h> | ||||
| #include <linux/dcache.h> | ||||
| @ -689,14 +690,28 @@ write_retry: | ||||
| 	ret = generic_perform_write(file, from, pos); | ||||
| 	if (likely(ret >= 0)) | ||||
| 		iocb->ki_pos = pos + ret; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If we just got an ENOSPC, try to write back all dirty inodes to | ||||
| 	 * convert delalloc space to free up some of the excess reserved | ||||
| 	 * metadata space. | ||||
| 	 * If we hit a space limit, try to free up some lingering preallocated | ||||
| 	 * space before returning an error. In the case of ENOSPC, first try to | ||||
| 	 * write back all dirty inodes to free up some of the excess reserved | ||||
| 	 * metadata space. This reduces the chances that the eofblocks scan | ||||
| 	 * waits on dirty mappings. Since xfs_flush_inodes() is serialized, this | ||||
| 	 * also behaves as a filter to prevent too many eofblocks scans from | ||||
| 	 * running at the same time. | ||||
| 	 */ | ||||
| 	if (ret == -ENOSPC && !enospc) { | ||||
| 	if (ret == -EDQUOT && !enospc) { | ||||
| 		enospc = xfs_inode_free_quota_eofblocks(ip); | ||||
| 		if (enospc) | ||||
| 			goto write_retry; | ||||
| 	} else if (ret == -ENOSPC && !enospc) { | ||||
| 		struct xfs_eofblocks eofb = {0}; | ||||
| 
 | ||||
| 		enospc = 1; | ||||
| 		xfs_flush_inodes(ip->i_mount); | ||||
| 		eofb.eof_scan_owner = ip->i_ino; /* for locking */ | ||||
| 		eofb.eof_flags = XFS_EOF_FLAGS_SYNC; | ||||
| 		xfs_icache_free_eofblocks(ip->i_mount, &eofb); | ||||
| 		goto write_retry; | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -375,6 +375,9 @@ struct xfs_fs_eofblocks { | ||||
| #define XFS_EOF_FLAGS_GID		(1 << 2) /* filter by gid */ | ||||
| #define XFS_EOF_FLAGS_PRID		(1 << 3) /* filter by project id */ | ||||
| #define XFS_EOF_FLAGS_MINFILESIZE	(1 << 4) /* filter by min file size */ | ||||
| #define XFS_EOF_FLAGS_UNION		(1 << 5) /* union filter algorithm; | ||||
| 						  * kernel only, not included in | ||||
| 						  * valid mask */ | ||||
| #define XFS_EOF_FLAGS_VALID	\ | ||||
| 	(XFS_EOF_FLAGS_SYNC |	\ | ||||
| 	 XFS_EOF_FLAGS_UID |	\ | ||||
|  | ||||
| @ -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> | ||||
| @ -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,16 +1259,28 @@ 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)) | ||||
| @ -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) | ||||
|  | ||||
| @ -27,6 +27,7 @@ struct xfs_eofblocks { | ||||
| 	kgid_t		eof_gid; | ||||
| 	prid_t		eof_prid; | ||||
| 	__u64		eof_min_file_size; | ||||
| 	xfs_ino_t	eof_scan_owner; | ||||
| }; | ||||
| 
 | ||||
| #define SYNC_WAIT		0x0001	/* wait for i/o to complete */ | ||||
| @ -57,6 +58,7 @@ void xfs_inode_set_reclaim_tag(struct xfs_inode *ip); | ||||
| void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip); | ||||
| void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip); | ||||
| int xfs_icache_free_eofblocks(struct xfs_mount *, struct xfs_eofblocks *); | ||||
| int xfs_inode_free_quota_eofblocks(struct xfs_inode *ip); | ||||
| void xfs_eofblocks_worker(struct work_struct *); | ||||
| 
 | ||||
| int xfs_inode_ag_iterator(struct xfs_mount *mp, | ||||
| @ -84,6 +86,7 @@ xfs_fs_eofblocks_from_user( | ||||
| 	dst->eof_flags = src->eof_flags; | ||||
| 	dst->eof_prid = src->eof_prid; | ||||
| 	dst->eof_min_file_size = src->eof_min_file_size; | ||||
| 	dst->eof_scan_owner = NULLFSINO; | ||||
| 
 | ||||
| 	dst->eof_uid = INVALID_UID; | ||||
| 	if (src->eof_flags & XFS_EOF_FLAGS_UID) { | ||||
|  | ||||
| @ -397,7 +397,8 @@ xfs_quota_calc_throttle( | ||||
| 	struct xfs_inode *ip, | ||||
| 	int type, | ||||
| 	xfs_fsblock_t *qblocks, | ||||
| 	int *qshift) | ||||
| 	int *qshift, | ||||
| 	int64_t	*qfreesp) | ||||
| { | ||||
| 	int64_t freesp; | ||||
| 	int shift = 0; | ||||
| @ -406,6 +407,7 @@ xfs_quota_calc_throttle( | ||||
| 	/* over hi wmark, squash the prealloc completely */ | ||||
| 	if (dq->q_res_bcount >= dq->q_prealloc_hi_wmark) { | ||||
| 		*qblocks = 0; | ||||
| 		*qfreesp = 0; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| @ -418,6 +420,9 @@ xfs_quota_calc_throttle( | ||||
| 			shift += 2; | ||||
| 	} | ||||
| 
 | ||||
| 	if (freesp < *qfreesp) | ||||
| 		*qfreesp = freesp; | ||||
| 
 | ||||
| 	/* only overwrite the throttle values if we are more aggressive */ | ||||
| 	if ((freesp >> shift) < (*qblocks >> *qshift)) { | ||||
| 		*qblocks = freesp; | ||||
| @ -476,15 +481,18 @@ xfs_iomap_prealloc_size( | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check each quota to cap the prealloc size and provide a shift | ||||
| 	 * value to throttle with. | ||||
| 	 * Check each quota to cap the prealloc size, provide a shift value to | ||||
| 	 * throttle with and adjust amount of available space. | ||||
| 	 */ | ||||
| 	if (xfs_quota_need_throttle(ip, XFS_DQ_USER, alloc_blocks)) | ||||
| 		xfs_quota_calc_throttle(ip, XFS_DQ_USER, &qblocks, &qshift); | ||||
| 		xfs_quota_calc_throttle(ip, XFS_DQ_USER, &qblocks, &qshift, | ||||
| 					&freesp); | ||||
| 	if (xfs_quota_need_throttle(ip, XFS_DQ_GROUP, alloc_blocks)) | ||||
| 		xfs_quota_calc_throttle(ip, XFS_DQ_GROUP, &qblocks, &qshift); | ||||
| 		xfs_quota_calc_throttle(ip, XFS_DQ_GROUP, &qblocks, &qshift, | ||||
| 					&freesp); | ||||
| 	if (xfs_quota_need_throttle(ip, XFS_DQ_PROJ, alloc_blocks)) | ||||
| 		xfs_quota_calc_throttle(ip, XFS_DQ_PROJ, &qblocks, &qshift); | ||||
| 		xfs_quota_calc_throttle(ip, XFS_DQ_PROJ, &qblocks, &qshift, | ||||
| 					&freesp); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The final prealloc size is set to the minimum of free space available | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user