]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/xfs/libxfs/xfs_alloc.c
Merge tag 'samsung-soc-4.10-2' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk...
[karo-tx-linux.git] / fs / xfs / libxfs / xfs_alloc.c
index 05b5243d89f6ffcb4d77e71455ef34ec88165ebe..5050056a0b06445a93845987a52ec8356c499f80 100644 (file)
@@ -37,6 +37,7 @@
 #include "xfs_trans.h"
 #include "xfs_buf_item.h"
 #include "xfs_log.h"
+#include "xfs_ag_resv.h"
 
 struct workqueue_struct *xfs_alloc_wq;
 
@@ -51,10 +52,23 @@ 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 *);
 
+unsigned int
+xfs_refc_block(
+       struct xfs_mount        *mp)
+{
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+               return XFS_RMAP_BLOCK(mp) + 1;
+       if (xfs_sb_version_hasfinobt(&mp->m_sb))
+               return XFS_FIBT_BLOCK(mp) + 1;
+       return XFS_IBT_BLOCK(mp) + 1;
+}
+
 xfs_extlen_t
 xfs_prealloc_blocks(
        struct xfs_mount        *mp)
 {
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               return xfs_refc_block(mp) + 1;
        if (xfs_sb_version_hasrmapbt(&mp->m_sb))
                return XFS_RMAP_BLOCK(mp) + 1;
        if (xfs_sb_version_hasfinobt(&mp->m_sb))
@@ -74,14 +88,8 @@ xfs_prealloc_blocks(
  * extents need to be actually allocated. To get around this, we explicitly set
  * aside a few blocks which will not be reserved in delayed allocation.
  *
- * When rmap is disabled, we need to reserve 4 fsbs _per AG_ for the freelist
- * and 4 more to handle a potential split of the file's bmap btree.
- *
- * When rmap is enabled, we must also be able to handle two rmap btree inserts
- * to record both the file data extent and a new bmbt block.  The bmbt block
- * might not be in the same AG as the file data extent.  In the worst case
- * the bmap btree splits multiple levels and all the new blocks come from
- * different AGs, so set aside enough to handle rmap btree splits in all AGs.
+ * We need to reserve 4 fsbs _per AG_ for the freelist and 4 more to handle a
+ * potential split of the file's bmap btree.
  */
 unsigned int
 xfs_alloc_set_aside(
@@ -90,8 +98,6 @@ xfs_alloc_set_aside(
        unsigned int            blocks;
 
        blocks = 4 + (mp->m_sb.sb_agcount * XFS_ALLOC_AGFL_RESERVE);
-       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
-               blocks += mp->m_sb.sb_agcount * mp->m_rmap_maxlevels;
        return blocks;
 }
 
@@ -122,6 +128,8 @@ xfs_alloc_ag_max_usable(
                blocks++;               /* finobt root block */
        if (xfs_sb_version_hasrmapbt(&mp->m_sb))
                blocks++;               /* rmap root block */
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               blocks++;               /* refcount root block */
 
        return mp->m_sb.sb_agblocks - blocks;
 }
@@ -265,7 +273,7 @@ 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? */
+       int             datatype,       /* 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 */
@@ -276,6 +284,7 @@ xfs_alloc_compute_diff(
        xfs_extlen_t    newlen1=0;      /* length with newbno1 */
        xfs_extlen_t    newlen2=0;      /* length with newbno2 */
        xfs_agblock_t   wantend;        /* end of target extent */
+       bool            userdata = xfs_alloc_is_userdata(datatype);
 
        ASSERT(freelen >= wantlen);
        freeend = freebno + freelen;
@@ -680,12 +689,29 @@ xfs_alloc_ag_vextent(
        xfs_alloc_arg_t *args)  /* argument structure for allocation */
 {
        int             error=0;
+       xfs_extlen_t    reservation;
+       xfs_extlen_t    oldmax;
 
        ASSERT(args->minlen > 0);
        ASSERT(args->maxlen > 0);
        ASSERT(args->minlen <= args->maxlen);
        ASSERT(args->mod < args->prod);
        ASSERT(args->alignment > 0);
+
+       /*
+        * Clamp maxlen to the amount of free space minus any reservations
+        * that have been made.
+        */
+       oldmax = args->maxlen;
+       reservation = xfs_ag_resv_needed(args->pag, args->resv);
+       if (args->maxlen > args->pag->pagf_freeblks - reservation)
+               args->maxlen = args->pag->pagf_freeblks - reservation;
+       if (args->maxlen == 0) {
+               args->agbno = NULLAGBLOCK;
+               args->maxlen = oldmax;
+               return 0;
+       }
+
        /*
         * Branch to correct routine based on the type.
         */
@@ -705,12 +731,14 @@ xfs_alloc_ag_vextent(
                /* NOTREACHED */
        }
 
+       args->maxlen = oldmax;
+
        if (error || args->agbno == NULLAGBLOCK)
                return error;
 
        ASSERT(args->len >= args->minlen);
        ASSERT(args->len <= args->maxlen);
-       ASSERT(!args->wasfromfl || !args->isfl);
+       ASSERT(!args->wasfromfl || args->resv != XFS_AG_RESV_AGFL);
        ASSERT(args->agbno % args->alignment == 0);
 
        /* if not file data, insert new block into the reverse map btree */
@@ -732,12 +760,7 @@ xfs_alloc_ag_vextent(
                                              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_ag_resv_alloc_extent(args->pag, args->resv, args);
 
        XFS_STATS_INC(args->mp, xs_allocx);
        XFS_STATS_ADD(args->mp, xs_allocb, args->len);
@@ -917,7 +940,7 @@ xfs_alloc_find_best_extent(
 
                        sdiff = xfs_alloc_compute_diff(args->agbno, args->len,
                                                       args->alignment,
-                                                      args->userdata, *sbnoa,
+                                                      args->datatype, *sbnoa,
                                                       *slena, &new);
 
                        /*
@@ -1101,7 +1124,7 @@ restart:
                        if (args->len < blen)
                                continue;
                        ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, args->userdata, ltbnoa,
+                               args->alignment, args->datatype, ltbnoa,
                                ltlena, &ltnew);
                        if (ltnew != NULLAGBLOCK &&
                            (args->len > blen || ltdiff < bdiff)) {
@@ -1254,7 +1277,7 @@ restart:
                        args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
                        xfs_alloc_fix_len(args);
                        ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, args->userdata, ltbnoa,
+                               args->alignment, args->datatype, ltbnoa,
                                ltlena, &ltnew);
 
                        error = xfs_alloc_find_best_extent(args,
@@ -1271,7 +1294,7 @@ restart:
                        args->len = XFS_EXTLEN_MIN(gtlena, args->maxlen);
                        xfs_alloc_fix_len(args);
                        gtdiff = xfs_alloc_compute_diff(args->agbno, args->len,
-                               args->alignment, args->userdata, gtbnoa,
+                               args->alignment, args->datatype, gtbnoa,
                                gtlena, &gtnew);
 
                        error = xfs_alloc_find_best_extent(args,
@@ -1331,7 +1354,7 @@ restart:
        }
        rlen = args->len;
        (void)xfs_alloc_compute_diff(args->agbno, rlen, args->alignment,
-                                    args->userdata, ltbnoa, ltlena, &ltnew);
+                                    args->datatype, ltbnoa, ltlena, &ltnew);
        ASSERT(ltnew >= ltbno);
        ASSERT(ltnew + rlen <= ltbnoa + ltlena);
        ASSERT(ltnew + rlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length));
@@ -1583,6 +1606,7 @@ xfs_alloc_ag_vextent_small(
        int             *stat)  /* status: 0-freelist, 1-normal/none */
 {
        struct xfs_owner_info   oinfo;
+       struct xfs_perag        *pag;
        int             error;
        xfs_agblock_t   fbno;
        xfs_extlen_t    flen;
@@ -1600,7 +1624,8 @@ xfs_alloc_ag_vextent_small(
         * to respect minleft even when pulling from the
         * freelist.
         */
-       else if (args->minlen == 1 && args->alignment == 1 && !args->isfl &&
+       else if (args->minlen == 1 && args->alignment == 1 &&
+                args->resv != XFS_AG_RESV_AGFL &&
                 (be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_flcount)
                  > args->minleft)) {
                error = xfs_alloc_get_freelist(args->tp, args->agbp, &fbno, 0);
@@ -1608,9 +1633,9 @@ xfs_alloc_ag_vextent_small(
                        goto error0;
                if (fbno != NULLAGBLOCK) {
                        xfs_extent_busy_reuse(args->mp, args->agno, fbno, 1,
-                                            args->userdata);
+                             xfs_alloc_allow_busy_reuse(args->datatype));
 
-                       if (args->userdata) {
+                       if (xfs_alloc_is_userdata(args->datatype)) {
                                xfs_buf_t       *bp;
 
                                bp = xfs_btree_get_bufs(args->mp, args->tp,
@@ -1629,13 +1654,18 @@ xfs_alloc_ag_vextent_small(
                        /*
                         * If we're feeding an AGFL block to something that
                         * doesn't live in the free space, we need to clear
-                        * out the OWN_AG rmap.
+                        * out the OWN_AG rmap and add the block back to
+                        * the AGFL per-AG reservation.
                         */
                        xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_AG);
                        error = xfs_rmap_free(args->tp, args->agbp, args->agno,
                                        fbno, 1, &oinfo);
                        if (error)
                                goto error0;
+                       pag = xfs_perag_get(args->mp, args->agno);
+                       xfs_ag_resv_free_extent(pag, XFS_AG_RESV_AGFL,
+                                       args->tp, 1);
+                       xfs_perag_put(pag);
 
                        *stat = 0;
                        return 0;
@@ -1683,7 +1713,7 @@ xfs_free_ag_extent(
        xfs_agblock_t           bno,
        xfs_extlen_t            len,
        struct xfs_owner_info   *oinfo,
-       int                     isfl)
+       enum xfs_ag_resv_type   type)
 {
        xfs_btree_cur_t *bno_cur;       /* cursor for by-block btree */
        xfs_btree_cur_t *cnt_cur;       /* cursor for by-size btree */
@@ -1911,21 +1941,22 @@ xfs_free_ag_extent(
         */
        pag = xfs_perag_get(mp, agno);
        error = xfs_alloc_update_counters(tp, pag, agbp, len);
+       xfs_ag_resv_free_extent(pag, type, tp, len);
        xfs_perag_put(pag);
        if (error)
                goto error0;
 
-       if (!isfl)
-               xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, (long)len);
        XFS_STATS_INC(mp, xs_freex);
        XFS_STATS_ADD(mp, xs_freeb, len);
 
-       trace_xfs_free_extent(mp, agno, bno, len, isfl, haveleft, haveright);
+       trace_xfs_free_extent(mp, agno, bno, len, type == XFS_AG_RESV_AGFL,
+                       haveleft, haveright);
 
        return 0;
 
  error0:
-       trace_xfs_free_extent(mp, agno, bno, len, isfl, -1, -1);
+       trace_xfs_free_extent(mp, agno, bno, len, type == XFS_AG_RESV_AGFL,
+                       -1, -1);
        if (bno_cur)
                xfs_btree_del_cursor(bno_cur, XFS_BTREE_ERROR);
        if (cnt_cur)
@@ -1950,21 +1981,43 @@ xfs_alloc_compute_maxlevels(
 }
 
 /*
- * Find the length of the longest extent in an AG.
+ * Find the length of the longest extent in an AG.  The 'need' parameter
+ * specifies how much space we're going to need for the AGFL and the
+ * 'reserved' parameter tells us how many blocks in this AG are reserved for
+ * other callers.
  */
 xfs_extlen_t
 xfs_alloc_longest_free_extent(
        struct xfs_mount        *mp,
        struct xfs_perag        *pag,
-       xfs_extlen_t            need)
+       xfs_extlen_t            need,
+       xfs_extlen_t            reserved)
 {
        xfs_extlen_t            delta = 0;
 
+       /*
+        * If the AGFL needs a recharge, we'll have to subtract that from the
+        * longest extent.
+        */
        if (need > pag->pagf_flcount)
                delta = need - pag->pagf_flcount;
 
+       /*
+        * If we cannot maintain others' reservations with space from the
+        * not-longest freesp extents, we'll have to subtract /that/ from
+        * the longest extent too.
+        */
+       if (pag->pagf_freeblks - pag->pagf_longest < reserved)
+               delta += reserved - (pag->pagf_freeblks - pag->pagf_longest);
+
+       /*
+        * If the longest extent is long enough to satisfy all the
+        * reservations and AGFL rules in place, we can return this extent.
+        */
        if (pag->pagf_longest > delta)
                return pag->pagf_longest - delta;
+
+       /* Otherwise, let the caller try for 1 block if there's space. */
        return pag->pagf_flcount > 0 || pag->pagf_longest > 0;
 }
 
@@ -2004,20 +2057,24 @@ xfs_alloc_space_available(
 {
        struct xfs_perag        *pag = args->pag;
        xfs_extlen_t            longest;
+       xfs_extlen_t            reservation; /* blocks that are still reserved */
        int                     available;
 
        if (flags & XFS_ALLOC_FLAG_FREEING)
                return true;
 
+       reservation = xfs_ag_resv_needed(pag, args->resv);
+
        /* do we have enough contiguous free space for the allocation? */
-       longest = xfs_alloc_longest_free_extent(args->mp, pag, min_free);
+       longest = xfs_alloc_longest_free_extent(args->mp, pag, min_free,
+                       reservation);
        if ((args->minlen + args->alignment + args->minalignslop - 1) > longest)
                return false;
 
-       /* do have enough free space remaining for the allocation? */
+       /* do we have enough free space remaining for the allocation? */
        available = (int)(pag->pagf_freeblks + pag->pagf_flcount -
-                         min_free - args->total);
-       if (available < (int)args->minleft)
+                         reservation - min_free - args->total);
+       if (available < (int)args->minleft || available <= 0)
                return false;
 
        return true;
@@ -2058,7 +2115,7 @@ xfs_alloc_fix_freelist(
         * somewhere else if we are not being asked to try harder at this
         * point
         */
-       if (pag->pagf_metadata && args->userdata &&
+       if (pag->pagf_metadata && xfs_alloc_is_userdata(args->datatype) &&
            (flags & XFS_ALLOC_FLAG_TRYLOCK)) {
                ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
                goto out_agbp_relse;
@@ -2124,7 +2181,7 @@ xfs_alloc_fix_freelist(
                if (error)
                        goto out_agbp_relse;
                error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1,
-                                          &targs.oinfo, 1);
+                                          &targs.oinfo, XFS_AG_RESV_AGFL);
                if (error)
                        goto out_agbp_relse;
                bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0);
@@ -2135,7 +2192,7 @@ xfs_alloc_fix_freelist(
        targs.mp = mp;
        targs.agbp = agbp;
        targs.agno = args->agno;
-       targs.alignment = targs.minlen = targs.prod = targs.isfl = 1;
+       targs.alignment = targs.minlen = targs.prod = 1;
        targs.type = XFS_ALLOCTYPE_THIS_AG;
        targs.pag = pag;
        error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp);
@@ -2146,6 +2203,7 @@ xfs_alloc_fix_freelist(
        while (pag->pagf_flcount < need) {
                targs.agbno = 0;
                targs.maxlen = need - pag->pagf_flcount;
+               targs.resv = XFS_AG_RESV_AGFL;
 
                /* Allocate as many blocks as possible at once. */
                error = xfs_alloc_ag_vextent(&targs);
@@ -2278,6 +2336,9 @@ xfs_alloc_log_agf(
                offsetof(xfs_agf_t, agf_btreeblks),
                offsetof(xfs_agf_t, agf_uuid),
                offsetof(xfs_agf_t, agf_rmap_blocks),
+               offsetof(xfs_agf_t, agf_refcount_blocks),
+               offsetof(xfs_agf_t, agf_refcount_root),
+               offsetof(xfs_agf_t, agf_refcount_level),
                /* needed so that we don't log the whole rest of the structure: */
                offsetof(xfs_agf_t, agf_spare64),
                sizeof(xfs_agf_t)
@@ -2394,12 +2455,15 @@ xfs_agf_verify(
              be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)))
                return false;
 
-       if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
+       if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) < 1 ||
+           be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) < 1 ||
+           be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
            be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) > XFS_BTREE_MAXLEVELS)
                return false;
 
        if (xfs_sb_version_hasrmapbt(&mp->m_sb) &&
-           be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS)
+           (be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) < 1 ||
+            be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS))
                return false;
 
        /*
@@ -2415,6 +2479,11 @@ xfs_agf_verify(
            be32_to_cpu(agf->agf_btreeblks) > be32_to_cpu(agf->agf_length))
                return false;
 
+       if (xfs_sb_version_hasreflink(&mp->m_sb) &&
+           (be32_to_cpu(agf->agf_refcount_level) < 1 ||
+            be32_to_cpu(agf->agf_refcount_level) > XFS_BTREE_MAXLEVELS))
+               return false;
+
        return true;;
 
 }
@@ -2535,6 +2604,7 @@ xfs_alloc_read_agf(
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi]);
                pag->pagf_levels[XFS_BTNUM_RMAPi] =
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAPi]);
+               pag->pagf_refcount_level = be32_to_cpu(agf->agf_refcount_level);
                spin_lock_init(&pag->pagb_lock);
                pag->pagb_count = 0;
                pag->pagb_tree = RB_ROOT;
@@ -2633,7 +2703,7 @@ xfs_alloc_vextent(
                 * Try near allocation first, then anywhere-in-ag after
                 * the first a.g. fails.
                 */
-               if ((args->userdata & XFS_ALLOC_INITIAL_USER_DATA) &&
+               if ((args->datatype & XFS_ALLOC_INITIAL_USER_DATA) &&
                    (mp->m_flags & XFS_MOUNT_32BITINODES)) {
                        args->fsbno = XFS_AGB_TO_FSB(mp,
                                        ((mp->m_agfrotor / rotorstep) %
@@ -2766,7 +2836,7 @@ xfs_alloc_vextent(
 #endif
 
                /* Zero the extent if we were asked to do so */
-               if (args->userdata & XFS_ALLOC_USERDATA_ZERO) {
+               if (args->datatype & XFS_ALLOC_USERDATA_ZERO) {
                        error = xfs_zero_extent(args->ip, args->fsbno, args->len);
                        if (error)
                                goto error0;
@@ -2825,7 +2895,8 @@ xfs_free_extent(
        struct xfs_trans        *tp,    /* transaction pointer */
        xfs_fsblock_t           bno,    /* starting block number of extent */
        xfs_extlen_t            len,    /* length of extent */
-       struct xfs_owner_info   *oinfo) /* extent owner */
+       struct xfs_owner_info   *oinfo, /* extent owner */
+       enum xfs_ag_resv_type   type)   /* block reservation type */
 {
        struct xfs_mount        *mp = tp->t_mountp;
        struct xfs_buf          *agbp;
@@ -2834,6 +2905,7 @@ xfs_free_extent(
        int                     error;
 
        ASSERT(len != 0);
+       ASSERT(type != XFS_AG_RESV_AGFL);
 
        if (XFS_TEST_ERROR(false, mp,
                        XFS_ERRTAG_FREE_EXTENT,
@@ -2851,7 +2923,7 @@ xfs_free_extent(
                agbno + len <= be32_to_cpu(XFS_BUF_TO_AGF(agbp)->agf_length),
                                err);
 
-       error = xfs_free_ag_extent(tp, agbp, agno, agbno, len, oinfo, 0);
+       error = xfs_free_ag_extent(tp, agbp, agno, agbno, len, oinfo, type);
        if (error)
                goto err;