Commit 14b064ce authored by Dave Chinner's avatar Dave Chinner Committed by Alex Elder

xfs: limit extent length for allocation to AG size

Delayed allocation extents can be larger than AGs, so when trying to
convert a large range we may scan every AG inside
xfs_bmap_alloc_nullfb() trying to find an AG with a size larger than
an AG. We should stop when we find the first AG with a maximum
possible allocation size. This causes excessive CPU usage when there
are lots of AGs.

The same problem occurs when doing preallocation of a range larger
than an AG.

Fix the problem by limiting real allocation lengths to the maximum
that an AG can support. This means if we have empty AGs, we'll stop
the search at the first of them. If there are no empty AGs, we'll
still scan them all, but that is a different problem....
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarAlex Elder <aelder@sgi.com>
parent b8fc8263
......@@ -74,6 +74,22 @@ typedef unsigned int xfs_alloctype_t;
*/
#define XFS_ALLOC_SET_ASIDE(mp) (4 + ((mp)->m_sb.sb_agcount * 4))
/*
* When deciding how much space to allocate out of an AG, we limit the
* allocation maximum size to the size the AG. However, we cannot use all the
* blocks in the AG - some are permanently used by metadata. These
* blocks are generally:
* - the AG superblock, AGF, AGI and AGFL
* - the AGF (bno and cnt) and AGI btree root blocks
* - 4 blocks on the AGFL according to XFS_ALLOC_SET_ASIDE() limits
*
* The AG headers are sector sized, so the amount of space they take up is
* dependent on filesystem geometry. The others are all single blocks.
*/
#define XFS_ALLOC_AG_MAX_USABLE(mp) \
((mp)->m_sb.sb_agblocks - XFS_BB_TO_FSB(mp, XFS_FSS_TO_BB(mp, 4)) - 7)
/*
* Argument structure for xfs_alloc routines.
* This is turned into a structure to avoid having 20 arguments passed
......
......@@ -2430,7 +2430,7 @@ xfs_bmap_btalloc_nullfb(
startag = ag = 0;
pag = xfs_perag_get(mp, ag);
while (*blen < ap->alen) {
while (*blen < args->maxlen) {
if (!pag->pagf_init) {
error = xfs_alloc_pagf_init(mp, args->tp, ag,
XFS_ALLOC_FLAG_TRYLOCK);
......@@ -2452,7 +2452,7 @@ xfs_bmap_btalloc_nullfb(
notinit = 1;
if (xfs_inode_is_filestream(ap->ip)) {
if (*blen >= ap->alen)
if (*blen >= args->maxlen)
break;
if (ap->userdata) {
......@@ -2498,14 +2498,14 @@ xfs_bmap_btalloc_nullfb(
* If the best seen length is less than the request
* length, use the best as the minimum.
*/
else if (*blen < ap->alen)
else if (*blen < args->maxlen)
args->minlen = *blen;
/*
* Otherwise we've seen an extent as big as alen,
* Otherwise we've seen an extent as big as maxlen,
* use that as the minimum.
*/
else
args->minlen = ap->alen;
args->minlen = args->maxlen;
/*
* set the failure fallback case to look in the selected
......@@ -2573,7 +2573,9 @@ xfs_bmap_btalloc(
args.tp = ap->tp;
args.mp = mp;
args.fsbno = ap->rval;
args.maxlen = MIN(ap->alen, mp->m_sb.sb_agblocks);
/* Trim the allocation back to the maximum an AG can fit. */
args.maxlen = MIN(ap->alen, XFS_ALLOC_AG_MAX_USABLE(mp));
args.firstblock = ap->firstblock;
blen = 0;
if (nullfb) {
......@@ -2621,7 +2623,7 @@ xfs_bmap_btalloc(
/*
* Adjust for alignment
*/
if (blen > args.alignment && blen <= ap->alen)
if (blen > args.alignment && blen <= args.maxlen)
args.minlen = blen - args.alignment;
args.minalignslop = 0;
} else {
......@@ -2640,7 +2642,7 @@ xfs_bmap_btalloc(
* of minlen+alignment+slop doesn't go up
* between the calls.
*/
if (blen > mp->m_dalign && blen <= ap->alen)
if (blen > mp->m_dalign && blen <= args.maxlen)
nextminlen = blen - mp->m_dalign;
else
nextminlen = args.minlen;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment