Speculative preallocation currently occurs based on the size of a file (8GB max) and is throttled only within 5% of ENOSPC. Enable similar throttling as an inode approaches EDQUOT. Preallocation is throttled to a quota hard limit and disabled if the hard limit is surpassed (noenforce). If a soft limit is also specified, it serves as a low watermark to enable throttling and is used to adjust the percentage of free quota space a single preallocation is allowed to consume (5% by default). The algorithm determines the max percentage allowed for each quota and calculates the associated raw values. The minimum raw value across all quotas applicable to the inode represents the maximum size allowed for a preallocation on that inode. Signed-off-by: Brian Foster <bfoster@xxxxxxxxxx> --- fs/xfs/xfs_iomap.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++- fs/xfs/xfs_iomap.h | 2 + 2 files changed, 115 insertions(+), 1 deletions(-) diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index d381326..bbeec02 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -42,6 +42,8 @@ #include "xfs_iomap.h" #include "xfs_trace.h" #include "xfs_icache.h" +#include "xfs_dquot_item.h" +#include "xfs_dquot.h" #define XFS_WRITEIO_ALIGN(mp,off) (((off) >> mp->m_writeio_log) \ @@ -311,10 +313,110 @@ xfs_iomap_eof_want_preallocate( } /* + * Return the maximum size preallocation allowed for a particular dquot. + * + * Quota throttling is enabled when a hard limit is defined. By default, a + * preallocation is allowed to consume no more than 5% of available space in + * the quota. + * + * If a soft limit is also defined, quota throttling is not enabled until the + * requested preallocation surpasses the soft limit. The throttling percentage + * is also redefined to equal the difference between the soft and hard limits + * over the hard limit (i.e., (hard - soft) / hard). + * + * -1 is returned if no throttling is required. 0 is returned if preallocation + * should be disabled. + */ +STATIC int64_t +xfs_prealloc_dquot_max( + struct xfs_dquot *dq, + xfs_fsblock_t alloc_blocks) +{ + xfs_qcnt_t hardlimit; + xfs_qcnt_t softlimit; + int64_t free; + int64_t pct = XFS_DEFAULT_QTHROTTLE_PCT; + + if (!dq) + return -1; + + hardlimit = be64_to_cpu(dq->q_core.d_blk_hardlimit); + softlimit = be64_to_cpu(dq->q_core.d_blk_softlimit); + + if (!hardlimit) + return -1; + + /* disable preallocation if we're over the hard limit */ + free = hardlimit - dq->q_res_bcount; + if (free < 0) + return 0; + + /* disable throttling if we're under the soft limit */ + if (softlimit && (dq->q_res_bcount + alloc_blocks) < softlimit) + return -1; + + /* + * If specified, use the difference between the soft and hard limits + * over the hard limit to determine the throttling percentage. The + * throttling percentage determines how much of the quota free space a + * single preallocation can consume. + */ + if (softlimit) { + pct = (hardlimit - softlimit) * 100; + do_div(pct, hardlimit); + } + ASSERT(pct >= 0 && pct <= 100); + + do_div(free, 100); + free *= pct; + + return free; +} + +/* + * Apply the quota preallocation throttling algorithm to each enabled quota and + * return the most restrictive value. The return value is the maximum size + * preallocation allowed for the inode. + */ +STATIC int64_t +xfs_prealloc_quota_max( + struct xfs_inode *ip, + xfs_fsblock_t alloc_blocks) +{ + int64_t free; + int64_t min_free = -1; + struct xfs_dquot *dq; + + if (XFS_IS_UQUOTA_ON(ip->i_mount)) { + dq = xfs_inode_dquot(ip, XFS_DQ_USER); + free = xfs_prealloc_dquot_max(dq, alloc_blocks); + if (free != -1 && (free < min_free || min_free == -1)) + min_free = free; + } + + if (XFS_IS_GQUOTA_ON(ip->i_mount)) { + dq = xfs_inode_dquot(ip, XFS_DQ_GROUP); + free = xfs_prealloc_dquot_max(dq, alloc_blocks); + if (free != -1 && (free < min_free || min_free == -1)) + min_free = free; + } + + if (XFS_IS_PQUOTA_ON(ip->i_mount)) { + dq = xfs_inode_dquot(ip, XFS_DQ_PROJ); + free = xfs_prealloc_dquot_max(dq, alloc_blocks); + if (free != -1 && (free < min_free || min_free == -1)) + min_free = free; + } + + return min_free; +} + +/* * If we don't have a user specified preallocation size, dynamically increase * the preallocation size as the size of the file grows. Cap the maximum size * at a single extent or less if the filesystem is near full. The closer the - * filesystem is to full, the smaller the maximum prealocation. + * filesystem is to full or a hard quota limit, the smaller the maximum + * preallocation. */ STATIC xfs_fsblock_t xfs_iomap_prealloc_size( @@ -324,6 +426,7 @@ xfs_iomap_prealloc_size( xfs_fsblock_t alloc_blocks = 0; int shift = 0; int64_t freesp; + int64_t max_quota_prealloc; if (mp->m_flags & XFS_MOUNT_DFLT_IOSIZE) goto check_writeio; @@ -348,6 +451,15 @@ xfs_iomap_prealloc_size( if (freesp < mp->m_low_space[XFS_LOWSP_1_PCNT]) shift++; } + + /* + * Throttle speculative allocation against the most restrictive quota + * limit. + */ + max_quota_prealloc = xfs_prealloc_quota_max(ip, alloc_blocks); + + if (max_quota_prealloc >= 0 && alloc_blocks >= max_quota_prealloc) + alloc_blocks = max_quota_prealloc; if (shift) alloc_blocks >>= shift; if (alloc_blocks) diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h index 8061576..07d79ea 100644 --- a/fs/xfs/xfs_iomap.h +++ b/fs/xfs/xfs_iomap.h @@ -18,6 +18,8 @@ #ifndef __XFS_IOMAP_H__ #define __XFS_IOMAP_H__ +#define XFS_DEFAULT_QTHROTTLE_PCT 5 /* default quota throttling % */ + struct xfs_inode; struct xfs_bmbt_irec; -- 1.7.7.6 _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs