xfs: convert xfs_ialloc_has_inodes_at_extent to return keyfill scan results
authorDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:15 +0000 (19:00 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:15 +0000 (19:00 -0700)
Convert the xfs_ialloc_has_inodes_at_extent function to return keyfill
scan results because for a given range of inode numbers, we might have
no indexed inodes at all; the entire region might be allocated ondisk
inodes; or there might be a mix of the two.

Unfortunately, sparse inodes adds to the complexity, because each inode
record can have holes, which means that we cannot use the generic btree
_scan_keyfill function because we must look for holes in individual
records to decide the result.  On the plus side, online fsck can now
detect sub-chunk discrepancies in the inobt.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
fs/xfs/libxfs/xfs_ialloc.c
fs/xfs/libxfs/xfs_ialloc.h
fs/xfs/scrub/ialloc.c

index be952bd..a16d5de 100644 (file)
@@ -2641,44 +2641,50 @@ xfs_ialloc_read_agi(
        return 0;
 }
 
-/* Is there an inode record covering a given range of inode numbers? */
-int
-xfs_ialloc_has_inode_record(
-       struct xfs_btree_cur    *cur,
-       xfs_agino_t             low,
-       xfs_agino_t             high,
-       bool                    *exists)
+/* How many inodes are backed by inode clusters ondisk? */
+STATIC int
+xfs_ialloc_count_ondisk(
+       struct xfs_btree_cur            *cur,
+       xfs_agino_t                     low,
+       xfs_agino_t                     high,
+       unsigned int                    *allocated)
 {
        struct xfs_inobt_rec_incore     irec;
-       xfs_agino_t             agino;
-       uint16_t                holemask;
-       int                     has_record;
-       int                     i;
-       int                     error;
+       unsigned int                    ret = 0;
+       int                             has_record;
+       int                             error;
 
-       *exists = false;
        error = xfs_inobt_lookup(cur, low, XFS_LOOKUP_LE, &has_record);
-       while (error == 0 && has_record) {
+       if (error)
+               return error;
+
+       while (has_record) {
+               unsigned int            i, hole_idx;
+
                error = xfs_inobt_get_rec(cur, &irec, &has_record);
-               if (error || irec.ir_startino > high)
+               if (error)
+                       return error;
+               if (irec.ir_startino > high)
                        break;
 
-               agino = irec.ir_startino;
-               holemask = irec.ir_holemask;
-               for (i = 0; i < XFS_INOBT_HOLEMASK_BITS; holemask >>= 1,
-                               i++, agino += XFS_INODES_PER_HOLEMASK_BIT) {
-                       if (holemask & 1)
+               for (i = 0; i < XFS_INODES_PER_CHUNK; i++) {
+                       if (irec.ir_startino + i < low)
                                continue;
-                       if (agino + XFS_INODES_PER_HOLEMASK_BIT > low &&
-                                       agino <= high) {
-                               *exists = true;
-                               return 0;
-                       }
+                       if (irec.ir_startino + i > high)
+                               break;
+
+                       hole_idx = i / XFS_INODES_PER_HOLEMASK_BIT;
+                       if (!(irec.ir_holemask & (1U << hole_idx)))
+                               ret++;
                }
 
                error = xfs_btree_increment(cur, 0, &has_record);
+               if (error)
+                       return error;
        }
-       return error;
+
+       *allocated = ret;
+       return 0;
 }
 
 /* Is there an inode record covering a given extent? */
@@ -2687,15 +2693,27 @@ xfs_ialloc_has_inodes_at_extent(
        struct xfs_btree_cur    *cur,
        xfs_agblock_t           bno,
        xfs_extlen_t            len,
-       bool                    *exists)
+       enum xbtree_recpacking  *outcome)
 {
-       xfs_agino_t             low;
-       xfs_agino_t             high;
+       xfs_agino_t             agino;
+       xfs_agino_t             last_agino;
+       unsigned int            allocated;
+       int                     error;
 
-       low = XFS_AGB_TO_AGINO(cur->bc_mp, bno);
-       high = XFS_AGB_TO_AGINO(cur->bc_mp, bno + len) - 1;
+       agino = XFS_AGB_TO_AGINO(cur->bc_mp, bno);
+       last_agino = XFS_AGB_TO_AGINO(cur->bc_mp, bno + len) - 1;
 
-       return xfs_ialloc_has_inode_record(cur, low, high, exists);
+       error = xfs_ialloc_count_ondisk(cur, agino, last_agino, &allocated);
+       if (error)
+               return error;
+
+       if (allocated == 0)
+               *outcome = XBTREE_RECPACKING_EMPTY;
+       else if (allocated == last_agino - agino + 1)
+               *outcome = XBTREE_RECPACKING_FULL;
+       else
+               *outcome = XBTREE_RECPACKING_SPARSE;
+       return 0;
 }
 
 struct xfs_ialloc_count_inodes {
index 90b0e50..fe824bb 100644 (file)
@@ -96,9 +96,8 @@ void xfs_inobt_btrec_to_irec(struct xfs_mount *mp,
 xfs_failaddr_t xfs_inobt_check_irec(struct xfs_btree_cur *cur,
                const struct xfs_inobt_rec_incore *irec);
 int xfs_ialloc_has_inodes_at_extent(struct xfs_btree_cur *cur,
-               xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
-int xfs_ialloc_has_inode_record(struct xfs_btree_cur *cur, xfs_agino_t low,
-               xfs_agino_t high, bool *exists);
+               xfs_agblock_t bno, xfs_extlen_t len,
+               enum xbtree_recpacking *outcome);
 int xfs_ialloc_count_inodes(struct xfs_btree_cur *cur, xfs_agino_t *count,
                xfs_agino_t *freecount);
 int xfs_inobt_insert_rec(struct xfs_btree_cur *cur, uint16_t holemask,
index 3a37c3d..fda96b5 100644 (file)
@@ -765,18 +765,18 @@ xchk_xref_inode_check(
        xfs_agblock_t           agbno,
        xfs_extlen_t            len,
        struct xfs_btree_cur    **icur,
-       bool                    should_have_inodes)
+       enum xbtree_recpacking  expected)
 {
-       bool                    has_inodes;
+       enum xbtree_recpacking  outcome;
        int                     error;
 
        if (!(*icur) || xchk_skip_xref(sc->sm))
                return;
 
-       error = xfs_ialloc_has_inodes_at_extent(*icur, agbno, len, &has_inodes);
+       error = xfs_ialloc_has_inodes_at_extent(*icur, agbno, len, &outcome);
        if (!xchk_should_check_xref(sc, &error, icur))
                return;
-       if (has_inodes != should_have_inodes)
+       if (outcome != expected)
                xchk_btree_xref_set_corrupt(sc, *icur, 0);
 }
 
@@ -787,8 +787,10 @@ xchk_xref_is_not_inode_chunk(
        xfs_agblock_t           agbno,
        xfs_extlen_t            len)
 {
-       xchk_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur, false);
-       xchk_xref_inode_check(sc, agbno, len, &sc->sa.fino_cur, false);
+       xchk_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur,
+                       XBTREE_RECPACKING_EMPTY);
+       xchk_xref_inode_check(sc, agbno, len, &sc->sa.fino_cur,
+                       XBTREE_RECPACKING_EMPTY);
 }
 
 /* xref check that the extent is covered by inodes */
@@ -798,5 +800,6 @@ xchk_xref_is_inode_chunk(
        xfs_agblock_t           agbno,
        xfs_extlen_t            len)
 {
-       xchk_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur, true);
+       xchk_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur,
+                       XBTREE_RECPACKING_FULL);
 }