]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: fix sparse inode limits on runt AG
authorDave Chinner <dchinner@redhat.com>
Mon, 25 Nov 2024 21:15:27 +0000 (13:15 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 24 Dec 2024 02:01:31 +0000 (18:01 -0800)
Source kernel commit: 13325333582d4820d39b9e8f63d6a54e745585d9

The runt AG at the end of a filesystem is almost always smaller than
the mp->m_sb.sb_agblocks. Unfortunately, when setting the max_agbno
limit for the inode chunk allocation, we do not take this into
account. This means we can allocate a sparse inode chunk that
overlaps beyond the end of an AG. When we go to allocate an inode
from that sparse chunk, the irec fails validation because the
agbno of the start of the irec is beyond valid limits for the runt
AG.

Prevent this from happening by taking into account the size of the
runt AG when allocating inode chunks. Also convert the various
checks for valid inode chunk agbnos to use xfs_ag_block_count()
so that they will also catch such issues in the future.

Fixes: 56d1115c9bc7 ("xfs: allocate sparse inode chunks on full chunk allocation failure")
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
libxfs/xfs_ialloc.c

index 2575447f92dfbb6ce00ef8e989215e494127105e..63ce76755eb77f55b3e2e7ae6334e4d0ae2d82ce 100644 (file)
@@ -848,7 +848,8 @@ sparse_alloc:
                 * the end of the AG.
                 */
                args.min_agbno = args.mp->m_sb.sb_inoalignmt;
-               args.max_agbno = round_down(args.mp->m_sb.sb_agblocks,
+               args.max_agbno = round_down(xfs_ag_block_count(args.mp,
+                                                       pag_agno(pag)),
                                            args.mp->m_sb.sb_inoalignmt) -
                                 igeo->ialloc_blks;
 
@@ -2344,9 +2345,9 @@ xfs_difree(
                return -EINVAL;
        }
        agbno = XFS_AGINO_TO_AGBNO(mp, agino);
-       if (agbno >= mp->m_sb.sb_agblocks)  {
-               xfs_warn(mp, "%s: agbno >= mp->m_sb.sb_agblocks (%d >= %d).",
-                       __func__, agbno, mp->m_sb.sb_agblocks);
+       if (agbno >= xfs_ag_block_count(mp, pag_agno(pag))) {
+               xfs_warn(mp, "%s: agbno >= xfs_ag_block_count (%d >= %d).",
+                       __func__, agbno, xfs_ag_block_count(mp, pag_agno(pag)));
                ASSERT(0);
                return -EINVAL;
        }
@@ -2469,7 +2470,7 @@ xfs_imap(
         */
        agino = XFS_INO_TO_AGINO(mp, ino);
        agbno = XFS_AGINO_TO_AGBNO(mp, agino);
-       if (agbno >= mp->m_sb.sb_agblocks ||
+       if (agbno >= xfs_ag_block_count(mp, pag_agno(pag)) ||
            ino != xfs_agino_to_ino(pag, agino)) {
                error = -EINVAL;
 #ifdef DEBUG
@@ -2479,11 +2480,12 @@ xfs_imap(
                 */
                if (flags & XFS_IGET_UNTRUSTED)
                        return error;
-               if (agbno >= mp->m_sb.sb_agblocks) {
+               if (agbno >= xfs_ag_block_count(mp, pag_agno(pag))) {
                        xfs_alert(mp,
                "%s: agbno (0x%llx) >= mp->m_sb.sb_agblocks (0x%lx)",
                                __func__, (unsigned long long)agbno,
-                               (unsigned long)mp->m_sb.sb_agblocks);
+                               (unsigned long)xfs_ag_block_count(mp,
+                                                       pag_agno(pag)));
                }
                if (ino != xfs_agino_to_ino(pag, agino)) {
                        xfs_alert(mp,