]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_repair: check the refcount btree against our observed reference counts when -n
authorDarrick J. Wong <darrick.wong@oracle.com>
Tue, 25 Oct 2016 22:14:35 +0000 (15:14 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Wed, 26 Oct 2016 23:41:07 +0000 (16:41 -0700)
Check the observed reference counts against whatever's in the refcount
btree for discrepancies.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
libxfs/libxfs_api_defs.h
repair/phase4.c
repair/rmap.c
repair/rmap.h
repair/scan.c

index b30228d1a555f44f3a50cea058d73d139e8c7e73..8c15b750860291c8d919d909e3c7e231822c94b7 100644 (file)
 #define xfs_dinode_good_version                libxfs_dinode_good_version
 #define xfs_free_extent                        libxfs_free_extent
 
+#define xfs_refcountbt_init_cursor     libxfs_refcountbt_init_cursor
+#define xfs_refcount_lookup_le         libxfs_refcount_lookup_le
+#define xfs_refcount_get_rec           libxfs_refcount_get_rec
+
 #endif /* __LIBXFS_API_DEFS_H__ */
index 2c2d611d86c4bacbb9b72e8212abd609e70c6fad..e59464b185fc70957cb645cf0b491be1d5387819 100644 (file)
@@ -222,6 +222,21 @@ _("%s while fixing inode reflink flags.\n"),
                         strerror(-error));
 }
 
+static void
+check_refcount_btrees(
+       work_queue_t    *wq,
+       xfs_agnumber_t  agno,
+       void            *arg)
+{
+       int             error;
+
+       error = check_refcounts(wq->mp, agno);
+       if (error)
+               do_error(
+_("%s while checking reference counts"),
+                        strerror(-error));
+}
+
 static void
 process_rmap_data(
        struct xfs_mount        *mp)
@@ -246,8 +261,10 @@ process_rmap_data(
        destroy_work_queue(&wq);
 
        create_work_queue(&wq, mp, libxfs_nproc());
-       for (i = 0; i < mp->m_sb.sb_agcount; i++)
+       for (i = 0; i < mp->m_sb.sb_agcount; i++) {
                queue_work(&wq, process_inode_reflink_flags, i, NULL);
+               queue_work(&wq, check_refcount_btrees, i, NULL);
+       }
        destroy_work_queue(&wq);
 }
 
index 68d9240298d6066fd2e410e9e811ea60d833d982..dd0be53c3651d04dbc636b839d90db2d5fefa680 100644 (file)
@@ -47,6 +47,7 @@ struct xfs_ag_rmap {
 
 static struct xfs_ag_rmap *ag_rmaps;
 static bool rmapbt_suspect;
+static bool refcbt_suspect;
 
 /*
  * Compare rmap observations for array sorting.
@@ -1242,6 +1243,131 @@ _("Unable to fix reflink flag on inode %"PRIu64".\n"),
        return error;
 }
 
+/*
+ * Return the number of refcount objects for an AG.
+ */
+size_t
+refcount_record_count(
+       struct xfs_mount                *mp,
+       xfs_agnumber_t          agno)
+{
+       return slab_count(ag_rmaps[agno].ar_refcount_items);
+}
+
+/*
+ * Return a slab cursor that will return refcount objects in order.
+ */
+int
+init_refcount_cursor(
+       xfs_agnumber_t          agno,
+       struct xfs_slab_cursor  **cur)
+{
+       return init_slab_cursor(ag_rmaps[agno].ar_refcount_items, NULL, cur);
+}
+
+/*
+ * Disable the refcount btree check.
+ */
+void
+refcount_avoid_check(void)
+{
+       refcbt_suspect = true;
+}
+
+/*
+ * Compare the observed reference counts against what's in the ag btree.
+ */
+int
+check_refcounts(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno)
+{
+       struct xfs_slab_cursor  *rl_cur;
+       struct xfs_btree_cur    *bt_cur = NULL;
+       int                     error;
+       int                     have;
+       int                     i;
+       struct xfs_buf          *agbp = NULL;
+       struct xfs_refcount_irec        *rl_rec;
+       struct xfs_refcount_irec        tmp;
+       struct xfs_perag        *pag;           /* per allocation group data */
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+       if (refcbt_suspect) {
+               if (no_modify && agno == 0)
+                       do_warn(_("would rebuild corrupt refcount btrees.\n"));
+               return 0;
+       }
+
+       /* Create cursors to refcount structures */
+       error = init_refcount_cursor(agno, &rl_cur);
+       if (error)
+               return error;
+
+       error = -libxfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               goto err;
+
+       /* Leave the per-ag data "uninitialized" since we rewrite it later */
+       pag = libxfs_perag_get(mp, agno);
+       pag->pagf_init = 0;
+       libxfs_perag_put(pag);
+
+       bt_cur = libxfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
+       if (!bt_cur) {
+               error = -ENOMEM;
+               goto err;
+       }
+
+       rl_rec = pop_slab_cursor(rl_cur);
+       while (rl_rec) {
+               /* Look for a refcount record in the btree */
+               error = -libxfs_refcount_lookup_le(bt_cur,
+                               rl_rec->rc_startblock, &have);
+               if (error)
+                       goto err;
+               if (!have) {
+                       do_warn(
+_("Missing reference count record for (%u/%u) len %u count %u\n"),
+                               agno, rl_rec->rc_startblock,
+                               rl_rec->rc_blockcount, rl_rec->rc_refcount);
+                       goto next_loop;
+               }
+
+               error = -libxfs_refcount_get_rec(bt_cur, &tmp, &i);
+               if (error)
+                       goto err;
+               if (!i) {
+                       do_warn(
+_("Missing reference count record for (%u/%u) len %u count %u\n"),
+                               agno, rl_rec->rc_startblock,
+                               rl_rec->rc_blockcount, rl_rec->rc_refcount);
+                       goto next_loop;
+               }
+
+               /* Compare each refcount observation against the btree's */
+               if (tmp.rc_startblock != rl_rec->rc_startblock ||
+                   tmp.rc_blockcount < rl_rec->rc_blockcount ||
+                   tmp.rc_refcount < rl_rec->rc_refcount)
+                       do_warn(
+_("Incorrect reference count: saw (%u/%u) len %u nlinks %u; should be (%u/%u) len %u nlinks %u\n"),
+                               agno, tmp.rc_startblock, tmp.rc_blockcount,
+                               tmp.rc_refcount, agno, rl_rec->rc_startblock,
+                               rl_rec->rc_blockcount, rl_rec->rc_refcount);
+next_loop:
+               rl_rec = pop_slab_cursor(rl_cur);
+       }
+
+err:
+       if (bt_cur)
+               libxfs_btree_del_cursor(bt_cur, XFS_BTREE_NOERROR);
+       if (agbp)
+               libxfs_putbuf(agbp);
+       free_slab_cursor(&rl_cur);
+       return 0;
+}
+
 /*
  * Regenerate the AGFL so that we don't run out of it while rebuilding the
  * rmap btree.  If skip_rmapbt is true, don't update the rmapbt (most probably
index 266448fa73c4e3170fce61bad1a6f13008181911..752ece8299202db92fac16dd26927e993e642f33 100644 (file)
@@ -50,6 +50,11 @@ extern void rmap_high_key_from_rec(struct xfs_rmap_irec *rec,
                struct xfs_rmap_irec *key);
 
 extern int compute_refcounts(struct xfs_mount *, xfs_agnumber_t);
+extern size_t refcount_record_count(struct xfs_mount *, xfs_agnumber_t);
+extern int init_refcount_cursor(xfs_agnumber_t, struct xfs_slab_cursor **);
+extern void refcount_avoid_check(void);
+extern int check_refcounts(struct xfs_mount *, xfs_agnumber_t);
+
 extern void record_inode_reflink_flag(struct xfs_mount *, struct xfs_dinode *,
        xfs_agnumber_t, xfs_agino_t, xfs_ino_t);
 extern int fix_inode_reflink_flags(struct xfs_mount *, xfs_agnumber_t);
index d3a1a82f61d5e4fc6bc0dc416bca8a5dabd3440c..1c60784f4de497c440fb1a28734f4a5e7c89cc6a 100644 (file)
@@ -1370,6 +1370,8 @@ _("%s btree block claimed (state %d), agno %d, bno %d, suspect %d\n"),
                }
        }
 out:
+       if (suspect)
+               refcount_avoid_check();
        return;
 }
 
@@ -2173,6 +2175,7 @@ validate_agf(
                } else  {
                        do_warn(_("bad agbno %u for refcntbt root, agno %d\n"),
                                bno, agno);
+                       refcount_avoid_check();
                }
        }