]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/rmap.c
libxfs: refactor manage_zones()
[thirdparty/xfsprogs-dev.git] / repair / rmap.c
index dd0be53c3651d04dbc636b839d90db2d5fefa680..19cceca32773292bbb773c57465a8bac28a3d6ae 100644 (file)
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2016 Oracle.  All Rights Reserved.
- *
  * Author: Darrick J. Wong <darrick.wong@oracle.com>
- *
- * 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; either version 2
- * of the License, or (at your option) any later version.
- *
- * 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 <libxfs.h>
 #include "btree.h"
@@ -26,6 +12,7 @@
 #include "dinode.h"
 #include "slab.h"
 #include "rmap.h"
+#include "bitmap.h"
 
 #undef RMAP_DEBUG
 
@@ -49,37 +36,9 @@ static struct xfs_ag_rmap *ag_rmaps;
 static bool rmapbt_suspect;
 static bool refcbt_suspect;
 
-/*
- * Compare rmap observations for array sorting.
- */
-static int
-rmap_compare(
-       const void              *a,
-       const void              *b)
+static inline int rmap_compare(const void *a, const void *b)
 {
-       const struct xfs_rmap_irec      *pa;
-       const struct xfs_rmap_irec      *pb;
-       __u64                   oa;
-       __u64                   ob;
-
-       pa = a; pb = b;
-       oa = libxfs_rmap_irec_offset_pack(pa);
-       ob = libxfs_rmap_irec_offset_pack(pb);
-
-       if (pa->rm_startblock < pb->rm_startblock)
-               return -1;
-       else if (pa->rm_startblock > pb->rm_startblock)
-               return 1;
-       else if (pa->rm_owner < pb->rm_owner)
-               return -1;
-       else if (pa->rm_owner > pb->rm_owner)
-               return 1;
-       else if (oa < ob)
-               return -1;
-       else if (oa > ob)
-               return 1;
-       else
-               return 0;
+       return libxfs_rmap_compare(a, b);
 }
 
 /*
@@ -371,7 +330,7 @@ err:
 
 static int
 find_first_zero_bit(
-       __uint64_t      mask)
+       uint64_t        mask)
 {
        int             n;
        int             b = 0;
@@ -384,7 +343,7 @@ find_first_zero_bit(
 
 static int
 popcnt(
-       __uint64_t      mask)
+       uint64_t        mask)
 {
        int             n;
        int             b = 0;
@@ -491,18 +450,17 @@ rmap_store_ag_btree_rec(
        struct xfs_buf          *agbp = NULL;
        struct xfs_buf          *agflbp = NULL;
        struct xfs_trans        *tp;
-       struct xfs_trans_res tres = {0};
        __be32                  *agfl_bno, *b;
+       struct xfs_ag_rmap      *ag_rmap = &ag_rmaps[agno];
+       struct bitmap           *own_ag_bitmap = NULL;
        int                     error = 0;
-       struct xfs_owner_info   oinfo;
 
        if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
                return 0;
 
        /* Release the ar_rmaps; they were put into the rmapbt during p5. */
-       free_slab(&ag_rmaps[agno].ar_rmaps);
-       error = init_slab(&ag_rmaps[agno].ar_rmaps,
-                                 sizeof(struct xfs_rmap_irec));
+       free_slab(&ag_rmap->ar_rmaps);
+       error = init_slab(&ag_rmap->ar_rmaps, sizeof(struct xfs_rmap_irec));
        if (error)
                goto err;
 
@@ -522,18 +480,50 @@ rmap_store_ag_btree_rec(
         * rmap, we only need to add rmap records for AGFL blocks past
         * that point in the AGFL because those blocks are a result of a
         * no-rmap no-shrink freelist fixup that we did earlier.
+        *
+        * However, some blocks end up on the AGFL because the free space
+        * btrees shed blocks as a result of allocating space to fix the
+        * freelist.  We already created in-core rmap records for the free
+        * space btree blocks, so we must be careful not to create those
+        * records again.  Create a bitmap of already-recorded OWN_AG rmaps.
         */
+       error = init_slab_cursor(ag_rmap->ar_raw_rmaps, rmap_compare, &rm_cur);
+       if (error)
+               goto err;
+       if (!bitmap_init(&own_ag_bitmap)) {
+               error = -ENOMEM;
+               goto err_slab;
+       }
+       while ((rm_rec = pop_slab_cursor(rm_cur)) != NULL) {
+               if (rm_rec->rm_owner != XFS_RMAP_OWN_AG)
+                       continue;
+               if (!bitmap_set(own_ag_bitmap, rm_rec->rm_startblock,
+                                       rm_rec->rm_blockcount)) {
+                       error = EFSCORRUPTED;
+                       goto err_slab;
+               }
+       }
+       free_slab_cursor(&rm_cur);
+
+       /* Create rmaps for any AGFL blocks that aren't already rmapped. */
        agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp);
-       b = agfl_bno + ag_rmaps[agno].ar_flcount;
-       while (*b != NULLAGBLOCK && b - agfl_bno < XFS_AGFL_SIZE(mp)) {
-               error = rmap_add_ag_rec(mp, agno, be32_to_cpu(*b), 1,
-                               XFS_RMAP_OWN_AG);
-               if (error)
-                       goto err;
+       b = agfl_bno + ag_rmap->ar_flcount;
+       while (*b != cpu_to_be32(NULLAGBLOCK) &&
+              b - agfl_bno < libxfs_agfl_size(mp)) {
+               xfs_agblock_t   agbno;
+
+               agbno = be32_to_cpu(*b);
+               if (!bitmap_test(own_ag_bitmap, agbno, 1)) {
+                       error = rmap_add_ag_rec(mp, agno, agbno, 1,
+                                       XFS_RMAP_OWN_AG);
+                       if (error)
+                               goto err;
+               }
                b++;
        }
        libxfs_putbuf(agflbp);
        agflbp = NULL;
+       bitmap_free(&own_ag_bitmap);
 
        /* Merge all the raw rmaps into the main list */
        error = rmap_fold_raw_recs(mp, agno);
@@ -541,15 +531,16 @@ rmap_store_ag_btree_rec(
                goto err;
 
        /* Create cursors to refcount structures */
-       error = init_slab_cursor(ag_rmaps[agno].ar_rmaps, rmap_compare,
-                       &rm_cur);
+       error = init_slab_cursor(ag_rmap->ar_rmaps, rmap_compare, &rm_cur);
        if (error)
                goto err;
 
        /* Insert rmaps into the btree one at a time */
        rm_rec = pop_slab_cursor(rm_cur);
        while (rm_rec) {
-               error = -libxfs_trans_alloc(mp, &tres, 16, 0, 0, &tp);
+               struct xfs_owner_info   oinfo = {};
+
+               error = -libxfs_trans_alloc_rollable(mp, 16, &tp);
                if (error)
                        goto err_slab;
 
@@ -558,7 +549,7 @@ rmap_store_ag_btree_rec(
                        goto err_trans;
 
                ASSERT(XFS_RMAP_NON_INODE_OWNER(rm_rec->rm_owner));
-               libxfs_rmap_ag_owner(&oinfo, rm_rec->rm_owner);
+               oinfo.oi_owner = rm_rec->rm_owner;
                error = -libxfs_rmap_alloc(tp, agbp, agno, rm_rec->rm_startblock,
                                rm_rec->rm_blockcount, &oinfo);
                if (error)
@@ -583,6 +574,8 @@ err_slab:
 err:
        if (agflbp)
                libxfs_putbuf(agflbp);
+       if (own_ag_bitmap)
+               bitmap_free(&own_ag_bitmap);
        return error;
 }
 
@@ -790,7 +783,7 @@ compute_refcounts(
                mark_inode_rl(mp, stack_top);
 
                /* Set nbno to the bno of the next refcount change */
-               if (n < slab_count(rmaps))
+               if (n < slab_count(rmaps) && array_cur)
                        nbno = array_cur->rm_startblock;
                else
                        nbno = NULLAGBLOCK;
@@ -917,6 +910,20 @@ rmap_lookup(
        return -libxfs_rmap_get_rec(bt_cur, tmp, have);
 }
 
+/* Look for an rmap in the rmapbt that matches a given rmap. */
+static int
+rmap_lookup_overlapped(
+       struct xfs_btree_cur    *bt_cur,
+       struct xfs_rmap_irec    *rm_rec,
+       struct xfs_rmap_irec    *tmp,
+       int                     *have)
+{
+       /* Have to use our fancy version for overlapped */
+       return -libxfs_rmap_lookup_le_range(bt_cur, rm_rec->rm_startblock,
+                               rm_rec->rm_owner, rm_rec->rm_offset,
+                               rm_rec->rm_flags, tmp, have);
+}
+
 /* Does the btree rmap cover the observed rmap? */
 #define NEXTP(x)       ((x)->rm_startblock + (x)->rm_blockcount)
 #define NEXTL(x)       ((x)->rm_offset + (x)->rm_blockcount)
@@ -1005,6 +1012,18 @@ rmaps_verify_btree(
                error = rmap_lookup(bt_cur, rm_rec, &tmp, &have);
                if (error)
                        goto err;
+               /*
+                * Using the range query is expensive, so only do it if
+                * the regular lookup doesn't find anything or if it doesn't
+                * match the observed rmap.
+                */
+               if (xfs_sb_version_hasreflink(&bt_cur->bc_mp->m_sb) &&
+                               (!have || !rmap_is_good(rm_rec, &tmp))) {
+                       error = rmap_lookup_overlapped(bt_cur, rm_rec,
+                                       &tmp, &have);
+                       if (error)
+                               goto err;
+               }
                if (!have) {
                        do_warn(
 _("Missing reverse-mapping record for (%u/%u) %slen %u owner %"PRId64" \
@@ -1066,14 +1085,14 @@ err:
  * Compare the key fields of two rmap records -- positive if key1 > key2,
  * negative if key1 < key2, and zero if equal.
  */
-__int64_t
+int64_t
 rmap_diffkeys(
        struct xfs_rmap_irec    *kp1,
        struct xfs_rmap_irec    *kp2)
 {
        __u64                   oa;
        __u64                   ob;
-       __int64_t               d;
+       int64_t                 d;
        struct xfs_rmap_irec    tmp;
 
        tmp = *kp1;
@@ -1083,7 +1102,7 @@ rmap_diffkeys(
        tmp.rm_flags &= ~XFS_RMAP_REC_FLAGS;
        ob = libxfs_rmap_irec_offset_pack(&tmp);
 
-       d = (__int64_t)kp1->rm_startblock - kp2->rm_startblock;
+       d = (int64_t)kp1->rm_startblock - kp2->rm_startblock;
        if (d)
                return d;
 
@@ -1194,10 +1213,10 @@ fix_inode_reflink_flags(
 {
        struct ino_tree_node    *irec;
        int                     bit;
-       __uint64_t              was;
-       __uint64_t              is;
-       __uint64_t              diff;
-       __uint64_t              mask;
+       uint64_t                was;
+       uint64_t                is;
+       uint64_t                diff;
+       uint64_t                mask;
        int                     error = 0;
        xfs_agino_t             agino;
 
@@ -1314,7 +1333,7 @@ check_refcounts(
        pag->pagf_init = 0;
        libxfs_perag_put(pag);
 
-       bt_cur = libxfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
+       bt_cur = libxfs_refcountbt_init_cursor(mp, NULL, agbp, agno);
        if (!bt_cur) {
                error = -ENOMEM;
                goto err;
@@ -1361,7 +1380,8 @@ next_loop:
 
 err:
        if (bt_cur)
-               libxfs_btree_del_cursor(bt_cur, XFS_BTREE_NOERROR);
+               libxfs_btree_del_cursor(bt_cur, error ? XFS_BTREE_ERROR :
+                                                       XFS_BTREE_NOERROR);
        if (agbp)
                libxfs_putbuf(agbp);
        free_slab_cursor(&rl_cur);
@@ -1381,7 +1401,6 @@ fix_freelist(
 {
        xfs_alloc_arg_t         args;
        xfs_trans_t             *tp;
-       struct xfs_trans_res    tres = {0};
        int                     flags;
        int                     error;
 
@@ -1390,8 +1409,7 @@ fix_freelist(
        args.agno = agno;
        args.alignment = 1;
        args.pag = libxfs_perag_get(mp, agno);
-       error = -libxfs_trans_alloc(mp, &tres,
-                       libxfs_alloc_min_freelist(mp, args.pag), 0, 0, &tp);
+       error = -libxfs_trans_alloc_rollable(mp, 0, &tp);
        if (error)
                do_error(_("failed to fix AGFL on AG %d, error %d\n"),
                                agno, error);
@@ -1427,7 +1445,9 @@ fix_freelist(
                do_error(_("failed to fix AGFL on AG %d, error %d\n"),
                                agno, error);
        }
-       libxfs_trans_commit(tp);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(_("%s: commit failed, error %d\n"), __func__, error);
 }
 
 /*