diff options
Diffstat (limited to 'fs/xfs/xfs_alloc.c')
| -rw-r--r-- | fs/xfs/xfs_alloc.c | 361 | 
1 files changed, 148 insertions, 213 deletions
| diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index 112abc439ca5..f3227984a9bf 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -41,10 +41,6 @@  #define	XFSA_FIXUP_BNO_OK	1  #define	XFSA_FIXUP_CNT_OK	2 -static int -xfs_alloc_busy_search(struct xfs_mount *mp, xfs_agnumber_t agno, -		    xfs_agblock_t bno, xfs_extlen_t len); -  /*   * Prototypes for per-ag allocation routines   */ @@ -94,7 +90,7 @@ xfs_alloc_lookup_ge(   * Lookup the first record less than or equal to [bno, len]   * in the btree given by cur.   */ -STATIC int				/* error */ +int					/* error */  xfs_alloc_lookup_le(  	struct xfs_btree_cur	*cur,	/* btree cursor */  	xfs_agblock_t		bno,	/* starting block of extent */ @@ -127,7 +123,7 @@ xfs_alloc_update(  /*   * Get the data from the pointed-to record.   */ -STATIC int				/* error */ +int					/* error */  xfs_alloc_get_rec(  	struct xfs_btree_cur	*cur,	/* btree cursor */  	xfs_agblock_t		*bno,	/* output: starting block of extent */ @@ -577,61 +573,58 @@ xfs_alloc_ag_vextent_exact(  	xfs_extlen_t	rlen;	/* length of returned extent */  	ASSERT(args->alignment == 1); +  	/*  	 * Allocate/initialize a cursor for the by-number freespace btree.  	 */  	bno_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp, -		args->agno, XFS_BTNUM_BNO); +					  args->agno, XFS_BTNUM_BNO); +  	/*  	 * Lookup bno and minlen in the btree (minlen is irrelevant, really).  	 * Look for the closest free block <= bno, it must contain bno  	 * if any free block does.  	 */ -	if ((error = xfs_alloc_lookup_le(bno_cur, args->agbno, args->minlen, &i))) +	error = xfs_alloc_lookup_le(bno_cur, args->agbno, args->minlen, &i); +	if (error)  		goto error0; -	if (!i) { -		/* -		 * Didn't find it, return null. -		 */ -		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR); -		args->agbno = NULLAGBLOCK; -		return 0; -	} +	if (!i) +		goto not_found; +  	/*  	 * Grab the freespace record.  	 */ -	if ((error = xfs_alloc_get_rec(bno_cur, &fbno, &flen, &i))) +	error = xfs_alloc_get_rec(bno_cur, &fbno, &flen, &i); +	if (error)  		goto error0;  	XFS_WANT_CORRUPTED_GOTO(i == 1, error0);  	ASSERT(fbno <= args->agbno);  	minend = args->agbno + args->minlen;  	maxend = args->agbno + args->maxlen;  	fend = fbno + flen; +  	/*  	 * Give up if the freespace isn't long enough for the minimum request.  	 */ -	if (fend < minend) { -		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR); -		args->agbno = NULLAGBLOCK; -		return 0; -	} +	if (fend < minend) +		goto not_found; +  	/*  	 * End of extent will be smaller of the freespace end and the  	 * maximal requested end. -	 */ -	end = XFS_AGBLOCK_MIN(fend, maxend); -	/* +	 *  	 * Fix the length according to mod and prod if given.  	 */ +	end = XFS_AGBLOCK_MIN(fend, maxend);  	args->len = end - args->agbno;  	xfs_alloc_fix_len(args); -	if (!xfs_alloc_fix_minleft(args)) { -		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR); -		return 0; -	} +	if (!xfs_alloc_fix_minleft(args)) +		goto not_found; +  	rlen = args->len;  	ASSERT(args->agbno + rlen <= fend);  	end = args->agbno + rlen; +  	/*  	 * We are allocating agbno for rlen [agbno .. end]  	 * Allocate/initialize a cursor for the by-size btree. @@ -640,16 +633,25 @@ xfs_alloc_ag_vextent_exact(  		args->agno, XFS_BTNUM_CNT);  	ASSERT(args->agbno + args->len <=  		be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); -	if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen, -			args->agbno, args->len, XFSA_FIXUP_BNO_OK))) { +	error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen, args->agbno, +				      args->len, XFSA_FIXUP_BNO_OK); +	if (error) {  		xfs_btree_del_cursor(cnt_cur, XFS_BTREE_ERROR);  		goto error0;  	} +  	xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);  	xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR); -	trace_xfs_alloc_exact_done(args);  	args->wasfromfl = 0; +	trace_xfs_alloc_exact_done(args); +	return 0; + +not_found: +	/* Didn't find it, return null. */ +	xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR); +	args->agbno = NULLAGBLOCK; +	trace_xfs_alloc_exact_notfound(args);  	return 0;  error0: @@ -659,6 +661,95 @@ error0:  }  /* + * Search the btree in a given direction via the search cursor and compare + * the records found against the good extent we've already found. + */ +STATIC int +xfs_alloc_find_best_extent( +	struct xfs_alloc_arg	*args,	/* allocation argument structure */ +	struct xfs_btree_cur	**gcur,	/* good cursor */ +	struct xfs_btree_cur	**scur,	/* searching cursor */ +	xfs_agblock_t		gdiff,	/* difference for search comparison */ +	xfs_agblock_t		*sbno,	/* extent found by search */ +	xfs_extlen_t		*slen, +	xfs_extlen_t		*slena,	/* aligned length */ +	int			dir)	/* 0 = search right, 1 = search left */ +{ +	xfs_agblock_t		bno; +	xfs_agblock_t		new; +	xfs_agblock_t		sdiff; +	int			error; +	int			i; + +	/* The good extent is perfect, no need to  search. */ +	if (!gdiff) +		goto out_use_good; + +	/* +	 * Look until we find a better one, run out of space or run off the end. +	 */ +	do { +		error = xfs_alloc_get_rec(*scur, sbno, slen, &i); +		if (error) +			goto error0; +		XFS_WANT_CORRUPTED_GOTO(i == 1, error0); +		xfs_alloc_compute_aligned(*sbno, *slen, args->alignment, +					  args->minlen, &bno, slena); + +		/* +		 * The good extent is closer than this one. +		 */ +		if (!dir) { +			if (bno >= args->agbno + gdiff) +				goto out_use_good; +		} else { +			if (bno <= args->agbno - gdiff) +				goto out_use_good; +		} + +		/* +		 * Same distance, compare length and pick the best. +		 */ +		if (*slena >= args->minlen) { +			args->len = XFS_EXTLEN_MIN(*slena, args->maxlen); +			xfs_alloc_fix_len(args); + +			sdiff = xfs_alloc_compute_diff(args->agbno, args->len, +						       args->alignment, *sbno, +						       *slen, &new); + +			/* +			 * Choose closer size and invalidate other cursor. +			 */ +			if (sdiff < gdiff) +				goto out_use_search; +			goto out_use_good; +		} + +		if (!dir) +			error = xfs_btree_increment(*scur, 0, &i); +		else +			error = xfs_btree_decrement(*scur, 0, &i); +		if (error) +			goto error0; +	} while (i); + +out_use_good: +	xfs_btree_del_cursor(*scur, XFS_BTREE_NOERROR); +	*scur = NULL; +	return 0; + +out_use_search: +	xfs_btree_del_cursor(*gcur, XFS_BTREE_NOERROR); +	*gcur = NULL; +	return 0; + +error0: +	/* caller invalidates cursors */ +	return error; +} + +/*   * Allocate a variable extent near bno in the allocation group agno.   * Extent's length (returned in len) will be between minlen and maxlen,   * and of the form k * prod + mod unless there's nothing that large. @@ -925,203 +1016,45 @@ xfs_alloc_ag_vextent_near(  			}  		}  	} while (bno_cur_lt || bno_cur_gt); +  	/*  	 * Got both cursors still active, need to find better entry.  	 */  	if (bno_cur_lt && bno_cur_gt) { -		/* -		 * Left side is long enough, look for a right side entry. -		 */  		if (ltlena >= args->minlen) {  			/* -			 * Fix up the length. +			 * Left side is good, look for a right side entry.  			 */  			args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);  			xfs_alloc_fix_len(args); -			rlen = args->len; -			ltdiff = xfs_alloc_compute_diff(args->agbno, rlen, +			ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,  				args->alignment, ltbno, ltlen, <new); + +			error = xfs_alloc_find_best_extent(args, +						&bno_cur_lt, &bno_cur_gt, +						ltdiff, >bno, >len, >lena, +						0 /* search right */); +		} else { +			ASSERT(gtlena >= args->minlen); +  			/* -			 * Not perfect. -			 */ -			if (ltdiff) { -				/* -				 * Look until we find a better one, run out of -				 * space, or run off the end. -				 */ -				while (bno_cur_lt && bno_cur_gt) { -					if ((error = xfs_alloc_get_rec( -							bno_cur_gt, >bno, -							>len, &i))) -						goto error0; -					XFS_WANT_CORRUPTED_GOTO(i == 1, error0); -					xfs_alloc_compute_aligned(gtbno, gtlen, -						args->alignment, args->minlen, -						>bnoa, >lena); -					/* -					 * The left one is clearly better. -					 */ -					if (gtbnoa >= args->agbno + ltdiff) { -						xfs_btree_del_cursor( -							bno_cur_gt, -							XFS_BTREE_NOERROR); -						bno_cur_gt = NULL; -						break; -					} -					/* -					 * If we reach a big enough entry, -					 * compare the two and pick the best. -					 */ -					if (gtlena >= args->minlen) { -						args->len = -							XFS_EXTLEN_MIN(gtlena, -								args->maxlen); -						xfs_alloc_fix_len(args); -						rlen = args->len; -						gtdiff = xfs_alloc_compute_diff( -							args->agbno, rlen, -							args->alignment, -							gtbno, gtlen, >new); -						/* -						 * Right side is better. -						 */ -						if (gtdiff < ltdiff) { -							xfs_btree_del_cursor( -								bno_cur_lt, -								XFS_BTREE_NOERROR); -							bno_cur_lt = NULL; -						} -						/* -						 * Left side is better. -						 */ -						else { -							xfs_btree_del_cursor( -								bno_cur_gt, -								XFS_BTREE_NOERROR); -							bno_cur_gt = NULL; -						} -						break; -					} -					/* -					 * Fell off the right end. -					 */ -					if ((error = xfs_btree_increment( -							bno_cur_gt, 0, &i))) -						goto error0; -					if (!i) { -						xfs_btree_del_cursor( -							bno_cur_gt, -							XFS_BTREE_NOERROR); -						bno_cur_gt = NULL; -						break; -					} -				} -			} -			/* -			 * The left side is perfect, trash the right side. -			 */ -			else { -				xfs_btree_del_cursor(bno_cur_gt, -						     XFS_BTREE_NOERROR); -				bno_cur_gt = NULL; -			} -		} -		/* -		 * It's the right side that was found first, look left. -		 */ -		else { -			/* -			 * Fix up the length. +			 * Right side is good, look for a left side entry.  			 */  			args->len = XFS_EXTLEN_MIN(gtlena, args->maxlen);  			xfs_alloc_fix_len(args); -			rlen = args->len; -			gtdiff = xfs_alloc_compute_diff(args->agbno, rlen, +			gtdiff = xfs_alloc_compute_diff(args->agbno, args->len,  				args->alignment, gtbno, gtlen, >new); -			/* -			 * Right side entry isn't perfect. -			 */ -			if (gtdiff) { -				/* -				 * Look until we find a better one, run out of -				 * space, or run off the end. -				 */ -				while (bno_cur_lt && bno_cur_gt) { -					if ((error = xfs_alloc_get_rec( -							bno_cur_lt, <bno, -							<len, &i))) -						goto error0; -					XFS_WANT_CORRUPTED_GOTO(i == 1, error0); -					xfs_alloc_compute_aligned(ltbno, ltlen, -						args->alignment, args->minlen, -						<bnoa, <lena); -					/* -					 * The right one is clearly better. -					 */ -					if (ltbnoa <= args->agbno - gtdiff) { -						xfs_btree_del_cursor( -							bno_cur_lt, -							XFS_BTREE_NOERROR); -						bno_cur_lt = NULL; -						break; -					} -					/* -					 * If we reach a big enough entry, -					 * compare the two and pick the best. -					 */ -					if (ltlena >= args->minlen) { -						args->len = XFS_EXTLEN_MIN( -							ltlena, args->maxlen); -						xfs_alloc_fix_len(args); -						rlen = args->len; -						ltdiff = xfs_alloc_compute_diff( -							args->agbno, rlen, -							args->alignment, -							ltbno, ltlen, <new); -						/* -						 * Left side is better. -						 */ -						if (ltdiff < gtdiff) { -							xfs_btree_del_cursor( -								bno_cur_gt, -								XFS_BTREE_NOERROR); -							bno_cur_gt = NULL; -						} -						/* -						 * Right side is better. -						 */ -						else { -							xfs_btree_del_cursor( -								bno_cur_lt, -								XFS_BTREE_NOERROR); -							bno_cur_lt = NULL; -						} -						break; -					} -					/* -					 * Fell off the left end. -					 */ -					if ((error = xfs_btree_decrement( -							bno_cur_lt, 0, &i))) -						goto error0; -					if (!i) { -						xfs_btree_del_cursor(bno_cur_lt, -							XFS_BTREE_NOERROR); -						bno_cur_lt = NULL; -						break; -					} -				} -			} -			/* -			 * The right side is perfect, trash the left side. -			 */ -			else { -				xfs_btree_del_cursor(bno_cur_lt, -					XFS_BTREE_NOERROR); -				bno_cur_lt = NULL; -			} + +			error = xfs_alloc_find_best_extent(args, +						&bno_cur_gt, &bno_cur_lt, +						gtdiff, <bno, <len, <lena, +						1 /* search left */);  		} + +		if (error) +			goto error0;  	} +  	/*  	 * If we couldn't get anything, give up.  	 */ @@ -1130,6 +1063,7 @@ xfs_alloc_ag_vextent_near(  		args->agbno = NULLAGBLOCK;  		return 0;  	} +  	/*  	 * At this point we have selected a freespace entry, either to the  	 * left or to the right.  If it's on the right, copy all the @@ -1146,6 +1080,7 @@ xfs_alloc_ag_vextent_near(  		j = 1;  	} else  		j = 0; +  	/*  	 * Fix up the length and compute the useful address.  	 */ @@ -2676,7 +2611,7 @@ restart:   * will require a synchronous transaction, but it can still be   * used to distinguish between a partial or exact match.   */ -static int +int  xfs_alloc_busy_search(  	struct xfs_mount	*mp,  	xfs_agnumber_t		agno, | 
