diff options
Diffstat (limited to 'fs/xfs/xfs_alloc.c')
-rw-r--r-- | fs/xfs/xfs_alloc.c | 2630 |
1 files changed, 0 insertions, 2630 deletions
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c deleted file mode 100644 index d43813267a80..000000000000 --- a/fs/xfs/xfs_alloc.c +++ /dev/null @@ -1,2630 +0,0 @@ -/* - * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "xfs.h" -#include "xfs_fs.h" -#include "xfs_format.h" -#include "xfs_log_format.h" -#include "xfs_shared.h" -#include "xfs_trans_resv.h" -#include "xfs_bit.h" -#include "xfs_sb.h" -#include "xfs_ag.h" -#include "xfs_mount.h" -#include "xfs_inode.h" -#include "xfs_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_alloc.h" -#include "xfs_extent_busy.h" -#include "xfs_error.h" -#include "xfs_cksum.h" -#include "xfs_trace.h" -#include "xfs_trans.h" -#include "xfs_buf_item.h" -#include "xfs_log.h" - -struct workqueue_struct *xfs_alloc_wq; - -#define XFS_ABSDIFF(a,b) (((a) <= (b)) ? ((b) - (a)) : ((a) - (b))) - -#define XFSA_FIXUP_BNO_OK 1 -#define XFSA_FIXUP_CNT_OK 2 - -STATIC int xfs_alloc_ag_vextent_exact(xfs_alloc_arg_t *); -STATIC int xfs_alloc_ag_vextent_near(xfs_alloc_arg_t *); -STATIC int xfs_alloc_ag_vextent_size(xfs_alloc_arg_t *); -STATIC int xfs_alloc_ag_vextent_small(xfs_alloc_arg_t *, - xfs_btree_cur_t *, xfs_agblock_t *, xfs_extlen_t *, int *); - -/* - * Lookup the record equal to [bno, len] in the btree given by cur. - */ -STATIC int /* error */ -xfs_alloc_lookup_eq( - struct xfs_btree_cur *cur, /* btree cursor */ - xfs_agblock_t bno, /* starting block of extent */ - xfs_extlen_t len, /* length of extent */ - int *stat) /* success/failure */ -{ - cur->bc_rec.a.ar_startblock = bno; - cur->bc_rec.a.ar_blockcount = len; - return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat); -} - -/* - * Lookup the first record greater than or equal to [bno, len] - * in the btree given by cur. - */ -int /* error */ -xfs_alloc_lookup_ge( - struct xfs_btree_cur *cur, /* btree cursor */ - xfs_agblock_t bno, /* starting block of extent */ - xfs_extlen_t len, /* length of extent */ - int *stat) /* success/failure */ -{ - cur->bc_rec.a.ar_startblock = bno; - cur->bc_rec.a.ar_blockcount = len; - return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat); -} - -/* - * Lookup the first record less than or equal to [bno, len] - * in the btree given by cur. - */ -int /* error */ -xfs_alloc_lookup_le( - struct xfs_btree_cur *cur, /* btree cursor */ - xfs_agblock_t bno, /* starting block of extent */ - xfs_extlen_t len, /* length of extent */ - int *stat) /* success/failure */ -{ - cur->bc_rec.a.ar_startblock = bno; - cur->bc_rec.a.ar_blockcount = len; - return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat); -} - -/* - * Update the record referred to by cur to the value given - * by [bno, len]. - * This either works (return 0) or gets an EFSCORRUPTED error. - */ -STATIC int /* error */ -xfs_alloc_update( - struct xfs_btree_cur *cur, /* btree cursor */ - xfs_agblock_t bno, /* starting block of extent */ - xfs_extlen_t len) /* length of extent */ -{ - union xfs_btree_rec rec; - - rec.alloc.ar_startblock = cpu_to_be32(bno); - rec.alloc.ar_blockcount = cpu_to_be32(len); - return xfs_btree_update(cur, &rec); -} - -/* - * Get the data from the pointed-to record. - */ -int /* error */ -xfs_alloc_get_rec( - struct xfs_btree_cur *cur, /* btree cursor */ - xfs_agblock_t *bno, /* output: starting block of extent */ - xfs_extlen_t *len, /* output: length of extent */ - int *stat) /* output: success/failure */ -{ - union xfs_btree_rec *rec; - int error; - - error = xfs_btree_get_rec(cur, &rec, stat); - if (!error && *stat == 1) { - *bno = be32_to_cpu(rec->alloc.ar_startblock); - *len = be32_to_cpu(rec->alloc.ar_blockcount); - } - return error; -} - -/* - * Compute aligned version of the found extent. - * Takes alignment and min length into account. - */ -STATIC void -xfs_alloc_compute_aligned( - xfs_alloc_arg_t *args, /* allocation argument structure */ - xfs_agblock_t foundbno, /* starting block in found extent */ - xfs_extlen_t foundlen, /* length in found extent */ - xfs_agblock_t *resbno, /* result block number */ - xfs_extlen_t *reslen) /* result length */ -{ - xfs_agblock_t bno; - xfs_extlen_t len; - - /* Trim busy sections out of found extent */ - xfs_extent_busy_trim(args, foundbno, foundlen, &bno, &len); - - if (args->alignment > 1 && len >= args->minlen) { - xfs_agblock_t aligned_bno = roundup(bno, args->alignment); - xfs_extlen_t diff = aligned_bno - bno; - - *resbno = aligned_bno; - *reslen = diff >= len ? 0 : len - diff; - } else { - *resbno = bno; - *reslen = len; - } -} - -/* - * Compute best start block and diff for "near" allocations. - * freelen >= wantlen already checked by caller. - */ -STATIC xfs_extlen_t /* difference value (absolute) */ -xfs_alloc_compute_diff( - xfs_agblock_t wantbno, /* target starting block */ - xfs_extlen_t wantlen, /* target length */ - xfs_extlen_t alignment, /* target alignment */ - char userdata, /* are we allocating data? */ - xfs_agblock_t freebno, /* freespace's starting block */ - xfs_extlen_t freelen, /* freespace's length */ - xfs_agblock_t *newbnop) /* result: best start block from free */ -{ - xfs_agblock_t freeend; /* end of freespace extent */ - xfs_agblock_t newbno1; /* return block number */ - xfs_agblock_t newbno2; /* other new block number */ - xfs_extlen_t newlen1=0; /* length with newbno1 */ - xfs_extlen_t newlen2=0; /* length with newbno2 */ - xfs_agblock_t wantend; /* end of target extent */ - - ASSERT(freelen >= wantlen); - freeend = freebno + freelen; - wantend = wantbno + wantlen; - /* - * We want to allocate from the start of a free extent if it is past - * the desired block or if we are allocating user data and the free - * extent is before desired block. The second case is there to allow - * for contiguous allocation from the remaining free space if the file - * grows in the short term. - */ - if (freebno >= wantbno || (userdata && freeend < wantend)) { - if ((newbno1 = roundup(freebno, alignment)) >= freeend) - newbno1 = NULLAGBLOCK; - } else if (freeend >= wantend && alignment > 1) { - newbno1 = roundup(wantbno, alignment); - newbno2 = newbno1 - alignment; - if (newbno1 >= freeend) - newbno1 = NULLAGBLOCK; - else - newlen1 = XFS_EXTLEN_MIN(wantlen, freeend - newbno1); - if (newbno2 < freebno) - newbno2 = NULLAGBLOCK; - else - newlen2 = XFS_EXTLEN_MIN(wantlen, freeend - newbno2); - if (newbno1 != NULLAGBLOCK && newbno2 != NULLAGBLOCK) { - if (newlen1 < newlen2 || - (newlen1 == newlen2 && - XFS_ABSDIFF(newbno1, wantbno) > - XFS_ABSDIFF(newbno2, wantbno))) - newbno1 = newbno2; - } else if (newbno2 != NULLAGBLOCK) - newbno1 = newbno2; - } else if (freeend >= wantend) { - newbno1 = wantbno; - } else if (alignment > 1) { - newbno1 = roundup(freeend - wantlen, alignment); - if (newbno1 > freeend - wantlen && - newbno1 - alignment >= freebno) - newbno1 -= alignment; - else if (newbno1 >= freeend) - newbno1 = NULLAGBLOCK; - } else - newbno1 = freeend - wantlen; - *newbnop = newbno1; - return newbno1 == NULLAGBLOCK ? 0 : XFS_ABSDIFF(newbno1, wantbno); -} - -/* - * Fix up the length, based on mod and prod. - * len should be k * prod + mod for some k. - * If len is too small it is returned unchanged. - * If len hits maxlen it is left alone. - */ -STATIC void -xfs_alloc_fix_len( - xfs_alloc_arg_t *args) /* allocation argument structure */ -{ - xfs_extlen_t k; - xfs_extlen_t rlen; - - ASSERT(args->mod < args->prod); - rlen = args->len; - ASSERT(rlen >= args->minlen); - ASSERT(rlen <= args->maxlen); - if (args->prod <= 1 || rlen < args->mod || rlen == args->maxlen || - (args->mod == 0 && rlen < args->prod)) - return; - k = rlen % args->prod; - if (k == args->mod) - return; - if (k > args->mod) - rlen = rlen - (k - args->mod); - else - rlen = rlen - args->prod + (args->mod - k); - if ((int)rlen < (int)args->minlen) - return; - ASSERT(rlen >= args->minlen && rlen <= args->maxlen); - ASSERT(rlen % args->prod == args->mod); - args->len = rlen; -} - -/* - * Fix up length if there is too little space left in the a.g. - * Return 1 if ok, 0 if too little, should give up. - */ -STATIC int -xfs_alloc_fix_minleft( - xfs_alloc_arg_t *args) /* allocation argument structure */ -{ - xfs_agf_t *agf; /* a.g. freelist header */ - int diff; /* free space difference */ - - if (args->minleft == 0) - return 1; - agf = XFS_BUF_TO_AGF(args->agbp); - diff = be32_to_cpu(agf->agf_freeblks) - - args->len - args->minleft; - if (diff >= 0) - return 1; - args->len += diff; /* shrink the allocated space */ - if (args->len >= args->minlen) - return 1; - args->agbno = NULLAGBLOCK; - return 0; -} - -/* - * Update the two btrees, logically removing from freespace the extent - * starting at rbno, rlen blocks. The extent is contained within the - * actual (current) free extent fbno for flen blocks. - * Flags are passed in indicating whether the cursors are set to the - * relevant records. - */ -STATIC int /* error code */ -xfs_alloc_fixup_trees( - xfs_btree_cur_t *cnt_cur, /* cursor for by-size btree */ - xfs_btree_cur_t *bno_cur, /* cursor for by-block btree */ - xfs_agblock_t fbno, /* starting block of free extent */ - xfs_extlen_t flen, /* length of free extent */ - xfs_agblock_t rbno, /* starting block of returned extent */ - xfs_extlen_t rlen, /* length of returned extent */ - int flags) /* flags, XFSA_FIXUP_... */ -{ - int error; /* error code */ - int i; /* operation results */ - xfs_agblock_t nfbno1; /* first new free startblock */ - xfs_agblock_t nfbno2; /* second new free startblock */ - xfs_extlen_t nflen1=0; /* first new free length */ - xfs_extlen_t nflen2=0; /* second new free length */ - - /* - * Look up the record in the by-size tree if necessary. - */ - if (flags & XFSA_FIXUP_CNT_OK) { -#ifdef DEBUG - if ((error = xfs_alloc_get_rec(cnt_cur, &nfbno1, &nflen1, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN( - i == 1 && nfbno1 == fbno && nflen1 == flen); -#endif - } else { - if ((error = xfs_alloc_lookup_eq(cnt_cur, fbno, flen, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 1); - } - /* - * Look up the record in the by-block tree if necessary. - */ - if (flags & XFSA_FIXUP_BNO_OK) { -#ifdef DEBUG - if ((error = xfs_alloc_get_rec(bno_cur, &nfbno1, &nflen1, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN( - i == 1 && nfbno1 == fbno && nflen1 == flen); -#endif - } else { - if ((error = xfs_alloc_lookup_eq(bno_cur, fbno, flen, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 1); - } - -#ifdef DEBUG - if (bno_cur->bc_nlevels == 1 && cnt_cur->bc_nlevels == 1) { - struct xfs_btree_block *bnoblock; - struct xfs_btree_block *cntblock; - - bnoblock = XFS_BUF_TO_BLOCK(bno_cur->bc_bufs[0]); - cntblock = XFS_BUF_TO_BLOCK(cnt_cur->bc_bufs[0]); - - XFS_WANT_CORRUPTED_RETURN( - bnoblock->bb_numrecs == cntblock->bb_numrecs); - } -#endif - - /* - * Deal with all four cases: the allocated record is contained - * within the freespace record, so we can have new freespace - * at either (or both) end, or no freespace remaining. - */ - if (rbno == fbno && rlen == flen) - nfbno1 = nfbno2 = NULLAGBLOCK; - else if (rbno == fbno) { - nfbno1 = rbno + rlen; - nflen1 = flen - rlen; - nfbno2 = NULLAGBLOCK; - } else if (rbno + rlen == fbno + flen) { - nfbno1 = fbno; - nflen1 = flen - rlen; - nfbno2 = NULLAGBLOCK; - } else { - nfbno1 = fbno; - nflen1 = rbno - fbno; - nfbno2 = rbno + rlen; - nflen2 = (fbno + flen) - nfbno2; - } - /* - * Delete the entry from the by-size btree. - */ - if ((error = xfs_btree_delete(cnt_cur, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 1); - /* - * Add new by-size btree entry(s). - */ - if (nfbno1 != NULLAGBLOCK) { - if ((error = xfs_alloc_lookup_eq(cnt_cur, nfbno1, nflen1, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 0); - if ((error = xfs_btree_insert(cnt_cur, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 1); - } - if (nfbno2 != NULLAGBLOCK) { - if ((error = xfs_alloc_lookup_eq(cnt_cur, nfbno2, nflen2, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 0); - if ((error = xfs_btree_insert(cnt_cur, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 1); - } - /* - * Fix up the by-block btree entry(s). - */ - if (nfbno1 == NULLAGBLOCK) { - /* - * No remaining freespace, just delete the by-block tree entry. - */ - if ((error = xfs_btree_delete(bno_cur, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 1); - } else { - /* - * Update the by-block entry to start later|be shorter. - */ - if ((error = xfs_alloc_update(bno_cur, nfbno1, nflen1))) - return error; - } - if (nfbno2 != NULLAGBLOCK) { - /* - * 2 resulting free entries, need to add one. - */ - if ((error = xfs_alloc_lookup_eq(bno_cur, nfbno2, nflen2, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 0); - if ((error = xfs_btree_insert(bno_cur, &i))) - return error; - XFS_WANT_CORRUPTED_RETURN(i == 1); - } - return 0; -} - -static bool -xfs_agfl_verify( - struct xfs_buf *bp) -{ - struct xfs_mount *mp = bp->b_target->bt_mount; - struct xfs_agfl *agfl = XFS_BUF_TO_AGFL(bp); - int i; - - if (!uuid_equal(&agfl->agfl_uuid, &mp->m_sb.sb_uuid)) - return false; - if (be32_to_cpu(agfl->agfl_magicnum) != XFS_AGFL_MAGIC) - return false; - /* - * during growfs operations, the perag is not fully initialised, - * so we can't use it for any useful checking. growfs ensures we can't - * use it by using uncached buffers that don't have the perag attached - * so we can detect and avoid this problem. - */ - if (bp->b_pag && be32_to_cpu(agfl->agfl_seqno) != bp->b_pag->pag_agno) - return false; - - for (i = 0; i < XFS_AGFL_SIZE(mp); i++) { - if (be32_to_cpu(agfl->agfl_bno[i]) != NULLAGBLOCK && - be32_to_cpu(agfl->agfl_bno[i]) >= mp->m_sb.sb_agblocks) - return false; - } - return true; -} - -static void -xfs_agfl_read_verify( - struct xfs_buf *bp) -{ - struct xfs_mount *mp = bp->b_target->bt_mount; - - /* - * There is no verification of non-crc AGFLs because mkfs does not - * initialise the AGFL to zero or NULL. Hence the only valid part of the - * AGFL is what the AGF says is active. We can't get to the AGF, so we - * can't verify just those entries are valid. - */ - if (!xfs_sb_version_hascrc(&mp->m_sb)) - return; - - if (!xfs_buf_verify_cksum(bp, XFS_AGFL_CRC_OFF)) - xfs_buf_ioerror(bp, EFSBADCRC); - else if (!xfs_agfl_verify(bp)) - xfs_buf_ioerror(bp, EFSCORRUPTED); - - if (bp->b_error) - xfs_verifier_error(bp); -} - -static void -xfs_agfl_write_verify( - struct xfs_buf *bp) -{ - struct xfs_mount *mp = bp->b_target->bt_mount; - struct xfs_buf_log_item *bip = bp->b_fspriv; - - /* no verification of non-crc AGFLs */ - if (!xfs_sb_version_hascrc(&mp->m_sb)) - return; - - if (!xfs_agfl_verify(bp)) { - xfs_buf_ioerror(bp, EFSCORRUPTED); - xfs_verifier_error(bp); - return; - } - - if (bip) - XFS_BUF_TO_AGFL(bp)->agfl_lsn = cpu_to_be64(bip->bli_item.li_lsn); - - xfs_buf_update_cksum(bp, XFS_AGFL_CRC_OFF); -} - -const struct xfs_buf_ops xfs_agfl_buf_ops = { - .verify_read = xfs_agfl_read_verify, - .verify_write = xfs_agfl_write_verify, -}; - -/* - * Read in the allocation group free block array. - */ -STATIC int /* error */ -xfs_alloc_read_agfl( - xfs_mount_t *mp, /* mount point structure */ - xfs_trans_t *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - xfs_buf_t **bpp) /* buffer for the ag free block array */ -{ - xfs_buf_t *bp; /* return value */ - int error; - - ASSERT(agno != NULLAGNUMBER); - error = xfs_trans_read_buf( - mp, tp, mp->m_ddev_targp, - XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)), - XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_agfl_buf_ops); - if (error) - return error; - xfs_buf_set_ref(bp, XFS_AGFL_REF); - *bpp = bp; - return 0; -} - -STATIC int -xfs_alloc_update_counters( - struct xfs_trans *tp, - struct xfs_perag *pag, - struct xfs_buf *agbp, - long len) -{ - struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp); - - pag->pagf_freeblks += len; - be32_add_cpu(&agf->agf_freeblks, len); - - xfs_trans_agblocks_delta(tp, len); - if (unlikely(be32_to_cpu(agf->agf_freeblks) > - be32_to_cpu(agf->agf_length))) - return EFSCORRUPTED; - - xfs_alloc_log_agf(tp, agbp, XFS_AGF_FREEBLKS); - return 0; -} - -/* - * Allocation group level functions. - */ - -/* - * Allocate a variable extent in the allocation group agno. - * Type and bno are used to determine where in the allocation group the - * extent will start. - * 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. - * Return the starting a.g. block, or NULLAGBLOCK if we can't do it. - */ -STATIC int /* error */ -xfs_alloc_ag_vextent( - xfs_alloc_arg_t *args) /* argument structure for allocation */ -{ - int error=0; - - ASSERT(args->minlen > 0); - ASSERT(args->maxlen > 0); - ASSERT(args->minlen <= args->maxlen); - ASSERT(args->mod < args->prod); - ASSERT(args->alignment > 0); - /* - * Branch to correct routine based on the type. - */ - args->wasfromfl = 0; - switch (args->type) { - case XFS_ALLOCTYPE_THIS_AG: - error = xfs_alloc_ag_vextent_size(args); - break; - case XFS_ALLOCTYPE_NEAR_BNO: - error = xfs_alloc_ag_vextent_near(args); - break; - case XFS_ALLOCTYPE_THIS_BNO: - error = xfs_alloc_ag_vextent_exact(args); - break; - default: - ASSERT(0); - /* NOTREACHED */ - } - - if (error || args->agbno == NULLAGBLOCK) - return error; - - ASSERT(args->len >= args->minlen); - ASSERT(args->len <= args->maxlen); - ASSERT(!args->wasfromfl || !args->isfl); - ASSERT(args->agbno % args->alignment == 0); - - if (!args->wasfromfl) { - error = xfs_alloc_update_counters(args->tp, args->pag, - args->agbp, - -((long)(args->len))); - if (error) - return error; - - ASSERT(!xfs_extent_busy_search(args->mp, args->agno, - args->agbno, args->len)); - } - - if (!args->isfl) { - xfs_trans_mod_sb(args->tp, args->wasdel ? - XFS_TRANS_SB_RES_FDBLOCKS : - XFS_TRANS_SB_FDBLOCKS, - -((long)(args->len))); - } - - XFS_STATS_INC(xs_allocx); - XFS_STATS_ADD(xs_allocb, args->len); - return error; -} - -/* - * Allocate a variable extent at exactly agno/bno. - * 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. - * Return the starting a.g. block (bno), or NULLAGBLOCK if we can't do it. - */ -STATIC int /* error */ -xfs_alloc_ag_vextent_exact( - xfs_alloc_arg_t *args) /* allocation argument structure */ -{ - xfs_btree_cur_t *bno_cur;/* by block-number btree cursor */ - xfs_btree_cur_t *cnt_cur;/* by count btree cursor */ - int error; - xfs_agblock_t fbno; /* start block of found extent */ - xfs_extlen_t flen; /* length of found extent */ - xfs_agblock_t tbno; /* start block of trimmed extent */ - xfs_extlen_t tlen; /* length of trimmed extent */ - xfs_agblock_t tend; /* end block of trimmed extent */ - int i; /* success/failure of operation */ - - 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); - - /* - * 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. - */ - error = xfs_alloc_lookup_le(bno_cur, args->agbno, args->minlen, &i); - if (error) - goto error0; - if (!i) - goto not_found; - - /* - * Grab the freespace record. - */ - 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); - - /* - * Check for overlapping busy extents. - */ - xfs_extent_busy_trim(args, fbno, flen, &tbno, &tlen); - - /* - * Give up if the start of the extent is busy, or the freespace isn't - * long enough for the minimum request. - */ - if (tbno > args->agbno) - goto not_found; - if (tlen < args->minlen) - goto not_found; - tend = tbno + tlen; - if (tend < args->agbno + args->minlen) - goto not_found; - - /* - * End of extent will be smaller of the freespace end and the - * maximal requested end. - * - * Fix the length according to mod and prod if given. - */ - args->len = XFS_AGBLOCK_MIN(tend, args->agbno + args->maxlen) - - args->agbno; - xfs_alloc_fix_len(args); - if (!xfs_alloc_fix_minleft(args)) - goto not_found; - - ASSERT(args->agbno + args->len <= tend); - - /* - * We are allocating agbno for args->len - * Allocate/initialize a cursor for the by-size btree. - */ - cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp, - args->agno, XFS_BTNUM_CNT); - ASSERT(args->agbno + args->len <= - be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); - 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); - - 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: - xfs_btree_del_cursor(bno_cur, XFS_BTREE_ERROR); - trace_xfs_alloc_exact_error(args); - return error; -} - -/* - * 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, /* extent length */ - xfs_agblock_t *sbnoa, /* aligned extent found by search */ - xfs_extlen_t *slena, /* aligned extent length */ - int dir) /* 0 = search right, 1 = search left */ -{ - 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(args, *sbno, *slen, sbnoa, slena); - - /* - * The good extent is closer than this one. - */ - if (!dir) { - if (*sbnoa >= args->agbno + gdiff) - goto out_use_good; - } else { - if (*sbnoa <= 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, - args->userdata, *sbnoa, - *slena, &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. - * Return the starting a.g. block, or NULLAGBLOCK if we can't do it. - */ -STATIC int /* error */ -xfs_alloc_ag_vextent_near( - xfs_alloc_arg_t *args) /* allocation argument structure */ -{ - xfs_btree_cur_t *bno_cur_gt; /* cursor for bno btree, right side */ - xfs_btree_cur_t *bno_cur_lt; /* cursor for bno btree, left side */ - xfs_btree_cur_t *cnt_cur; /* cursor for count btree */ - xfs_agblock_t gtbno; /* start bno of right side entry */ - xfs_agblock_t gtbnoa; /* aligned ... */ - xfs_extlen_t gtdiff; /* difference to right side entry */ - xfs_extlen_t gtlen; /* length of right side entry */ - xfs_extlen_t gtlena; /* aligned ... */ - xfs_agblock_t gtnew; /* useful start bno of right side */ - int error; /* error code */ - int i; /* result code, temporary */ - int j; /* result code, temporary */ - xfs_agblock_t ltbno; /* start bno of left side entry */ - xfs_agblock_t ltbnoa; /* aligned ... */ - xfs_extlen_t ltdiff; /* difference to left side entry */ - xfs_extlen_t ltlen; /* length of left side entry */ - xfs_extlen_t ltlena; /* aligned ... */ - xfs_agblock_t ltnew; /* useful start bno of left side */ - xfs_extlen_t rlen; /* length of returned extent */ - int forced = 0; -#ifdef DEBUG - /* - * Randomly don't execute the first algorithm. - */ - int dofirst; /* set to do first algorithm */ - - dofirst = prandom_u32() & 1; -#endif - -restart: - bno_cur_lt = NULL; - bno_cur_gt = NULL; - ltlen = 0; - gtlena = 0; - ltlena = 0; - - /* - * Get a cursor for the by-size btree. - */ - cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp, - args->agno, XFS_BTNUM_CNT); - - /* - * See if there are any free extents as big as maxlen. - */ - if ((error = xfs_alloc_lookup_ge(cnt_cur, 0, args->maxlen, &i))) - goto error0; - /* - * If none, then pick up the last entry in the tree unless the - * tree is empty. - */ - if (!i) { - if ((error = xfs_alloc_ag_vextent_small(args, cnt_cur, <bno, - <len, &i))) - goto error0; - if (i == 0 || ltlen == 0) { - xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR); - trace_xfs_alloc_near_noentry(args); - return 0; - } - ASSERT(i == 1); - } - args->wasfromfl = 0; - - /* - * First algorithm. - * If the requested extent is large wrt the freespaces available - * in this a.g., then the cursor will be pointing to a btree entry - * near the right edge of the tree. If it's in the last btree leaf - * block, then we just examine all the entries in that block - * that are big enough, and pick the best one. - * This is written as a while loop so we can break out of it, - * but we never loop back to the top. - */ - while (xfs_btree_islastblock(cnt_cur, 0)) { - xfs_extlen_t bdiff; - int besti=0; - xfs_extlen_t blen=0; - xfs_agblock_t bnew=0; - -#ifdef DEBUG - if (dofirst) - break; -#endif - /* - * Start from the entry that lookup found, sequence through - * all larger free blocks. If we're actually pointing at a - * record smaller than maxlen, go to the start of this block, - * and skip all those smaller than minlen. - */ - if (ltlen || args->alignment > 1) { - cnt_cur->bc_ptrs[0] = 1; - do { - if ((error = xfs_alloc_get_rec(cnt_cur, <bno, - <len, &i))) - goto error0; - XFS_WANT_CORRUPTED_GOTO(i == 1, error0); - if (ltlen >= args->minlen) - break; - if ((error = xfs_btree_increment(cnt_cur, 0, &i))) - goto error0; - } while (i); - ASSERT(ltlen >= args->minlen); - if (!i) - break; - } - i = cnt_cur->bc_ptrs[0]; - for (j = 1, blen = 0, bdiff = 0; - !error && j && (blen < args->maxlen || bdiff > 0); - error = xfs_btree_increment(cnt_cur, 0, &j)) { - /* - * For each entry, decide if it's better than - * the previous best entry. - */ - if ((error = xfs_alloc_get_rec(cnt_cur, <bno, <len, &i))) - goto error0; - XFS_WANT_CORRUPTED_GOTO(i == 1, error0); - xfs_alloc_compute_aligned(args, ltbno, ltlen, - <bnoa, <lena); - if (ltlena < args->minlen) - continue; - args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen); - xfs_alloc_fix_len(args); - ASSERT(args->len >= args->minlen); - if (args->len < blen) - continue; - ltdiff = xfs_alloc_compute_diff(args->agbno, args->len, - args->alignment, args->userdata, ltbnoa, - ltlena, <new); - if (ltnew != NULLAGBLOCK && - (args->len > blen || ltdiff < bdiff)) { - bdiff = ltdiff; - bnew = ltnew; - blen = args->len; - besti = cnt_cur->bc_ptrs[0]; - } - } - /* - * It didn't work. We COULD be in a case where - * there's a good record somewhere, so try again. - */ - if (blen == 0) - break; - /* - * Point at the best entry, and retrieve it again. - */ - cnt_cur->bc_ptrs[0] = besti; - if ((error = xfs_alloc_get_rec(cnt_cur, <bno, <len, &i))) - goto error0; - XFS_WANT_CORRUPTED_GOTO(i == 1, error0); - ASSERT(ltbno + ltlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); - args->len = blen; - if (!xfs_alloc_fix_minleft(args)) { - xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR); - trace_xfs_alloc_near_nominleft(args); - return 0; - } - blen = args->len; - /* - * We are allocating starting at bnew for blen blocks. - */ - args->agbno = bnew; - ASSERT(bnew >= ltbno); - ASSERT(bnew + blen <= ltbno + ltlen); - /* - * Set up a cursor for the by-bno tree. - */ - bno_cur_lt = xfs_allocbt_init_cursor(args->mp, args->tp, - args->agbp, args->agno, XFS_BTNUM_BNO); - /* - * Fix up the btree entries. - */ - if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur_lt, ltbno, - ltlen, bnew, blen, XFSA_FIXUP_CNT_OK))) - goto error0; - xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR); - xfs_btree_del_cursor(bno_cur_lt, XFS_BTREE_NOERROR); - - trace_xfs_alloc_near_first(args); - return 0; - } - /* - * Second algorithm. - * Search in the by-bno tree to the left and to the right - * simultaneously, until in each case we find a space big enough, - * or run into the edge of the tree. When we run into the edge, - * we deallocate that cursor. - * If both searches succeed, we compare the two spaces and pick - * the better one. - * With alignment, it's possible for both to fail; the upper - * level algorithm that picks allocation groups for allocations - * is not supposed to do this. - */ - /* - * Allocate and initialize the cursor for the leftward search. - */ - bno_cur_lt = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp, - args->agno, XFS_BTNUM_BNO); - /* - * Lookup <= bno to find the leftward search's starting point. - */ - if ((error = xfs_alloc_lookup_le(bno_cur_lt, args->agbno, args->maxlen, &i))) - goto error0; - if (!i) { - /* - * Didn't find anything; use this cursor for the rightward - * search. - */ - bno_cur_gt = bno_cur_lt; - bno_cur_lt = NULL; - } - /* - * Found something. Duplicate the cursor for the rightward search. - */ - else if ((error = xfs_btree_dup_cursor(bno_cur_lt, &bno_cur_gt))) - goto error0; - /* - * Increment the cursor, so we will point at the entry just right - * of the leftward entry if any, or to the leftmost entry. - */ - if ((error = xfs_btree_increment(bno_cur_gt, 0, &i))) - goto error0; - if (!i) { - /* - * It failed, there are no rightward entries. - */ - xfs_btree_del_cursor(bno_cur_gt, XFS_BTREE_NOERROR); - bno_cur_gt = NULL; - } - /* - * Loop going left with the leftward cursor, right with the - * rightward cursor, until either both directions give up or - * we find an entry at least as big as minlen. - */ - do { - if (bno_cur_lt) { - 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(args, ltbno, ltlen, - <bnoa, <lena); - if (ltlena >= args->minlen) - break; - if ((error = xfs_btree_decrement(bno_cur_lt, 0, &i))) - goto error0; - if (!i) { - xfs_btree_del_cursor(bno_cur_lt, - XFS_BTREE_NOERROR); |