ba3a5f4
From d0c7feaf87678371c2c09b3709400be416b2dc62 Mon Sep 17 00:00:00 2001
ba3a5f4
From: Zheng Bin <zhengbin13@huawei.com>
ba3a5f4
Date: Fri, 21 Feb 2020 07:38:20 -0800
ba3a5f4
Subject: [PATCH] xfs: add agf freeblocks verify in xfs_agf_verify
ba3a5f4
ba3a5f4
We recently used fuzz(hydra) to test XFS and automatically generate
ba3a5f4
tmp.img(XFS v5 format, but some metadata is wrong)
ba3a5f4
ba3a5f4
xfs_repair information(just one AG):
ba3a5f4
agf_freeblks 0, counted 3224 in ag 0
ba3a5f4
agf_longest 536874136, counted 3224 in ag 0
ba3a5f4
sb_fdblocks 613, counted 3228
ba3a5f4
ba3a5f4
Test as follows:
ba3a5f4
mount tmp.img tmpdir
ba3a5f4
cp file1M tmpdir
ba3a5f4
sync
ba3a5f4
ba3a5f4
In 4.19-stable, sync will stuck, the reason is:
ba3a5f4
xfs_mountfs
ba3a5f4
  xfs_check_summary_counts
ba3a5f4
    if ((!xfs_sb_version_haslazysbcount(&mp->m_sb) ||
ba3a5f4
       XFS_LAST_UNMOUNT_WAS_CLEAN(mp)) &&
ba3a5f4
       !xfs_fs_has_sickness(mp, XFS_SICK_FS_COUNTERS))
ba3a5f4
	return 0;  -->just return, incore sb_fdblocks still be 613
ba3a5f4
    xfs_initialize_perag_data
ba3a5f4
ba3a5f4
cp file1M tmpdir -->ok(write file to pagecache)
ba3a5f4
sync -->stuck(write pagecache to disk)
ba3a5f4
xfs_map_blocks
ba3a5f4
  xfs_iomap_write_allocate
ba3a5f4
    while (count_fsb != 0) {
ba3a5f4
      nimaps = 0;
ba3a5f4
      while (nimaps == 0) { --> endless loop
ba3a5f4
         nimaps = 1;
ba3a5f4
         xfs_bmapi_write(..., &nimaps) --> nimaps becomes 0 again
ba3a5f4
xfs_bmapi_write
ba3a5f4
  xfs_bmap_alloc
ba3a5f4
    xfs_bmap_btalloc
ba3a5f4
      xfs_alloc_vextent
ba3a5f4
        xfs_alloc_fix_freelist
ba3a5f4
          xfs_alloc_space_available -->fail(agf_freeblks is 0)
ba3a5f4
ba3a5f4
In linux-next, sync not stuck, cause commit c2b3164320b5 ("xfs:
ba3a5f4
use the latest extent at writeback delalloc conversion time") remove
ba3a5f4
the above while, dmesg is as follows:
ba3a5f4
[   55.250114] XFS (loop0): page discard on page ffffea0008bc7380, inode 0x1b0c, offset 0.
ba3a5f4
ba3a5f4
Users do not know why this page is discard, the better soultion is:
ba3a5f4
1. Like xfs_repair, make sure sb_fdblocks is equal to counted
ba3a5f4
(xfs_initialize_perag_data did this, who is not called at this mount)
ba3a5f4
2. Add agf verify, if fail, will tell users to repair
ba3a5f4
ba3a5f4
This patch use the second soultion.
ba3a5f4
ba3a5f4
Signed-off-by: Zheng Bin <zhengbin13@huawei.com>
ba3a5f4
Signed-off-by: Ren Xudong <renxudong1@huawei.com>
ba3a5f4
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
ba3a5f4
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
ba3a5f4
---
ba3a5f4
 fs/xfs/libxfs/xfs_alloc.c | 16 ++++++++++++++++
ba3a5f4
 1 file changed, 16 insertions(+)
ba3a5f4
ba3a5f4
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
ba3a5f4
index d8053bc96c4d..183dc2587092 100644
ba3a5f4
--- a/fs/xfs/libxfs/xfs_alloc.c
ba3a5f4
+++ b/fs/xfs/libxfs/xfs_alloc.c
ba3a5f4
@@ -2858,6 +2858,13 @@ xfs_agf_verify(
ba3a5f4
 	      be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp)))
ba3a5f4
 		return __this_address;
ba3a5f4
ba3a5f4
+	if (be32_to_cpu(agf->agf_length) > mp->m_sb.sb_dblocks)
ba3a5f4
+		return __this_address;
ba3a5f4
+
ba3a5f4
+	if (be32_to_cpu(agf->agf_freeblks) < be32_to_cpu(agf->agf_longest) ||
ba3a5f4
+	    be32_to_cpu(agf->agf_freeblks) > be32_to_cpu(agf->agf_length))
ba3a5f4
+		return __this_address;
ba3a5f4
+
ba3a5f4
 	if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) < 1 ||
ba3a5f4
 	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) < 1 ||
ba3a5f4
 	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
ba3a5f4
@@ -2869,6 +2876,10 @@ xfs_agf_verify(
ba3a5f4
 	     be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS))
ba3a5f4
 		return __this_address;
ba3a5f4
ba3a5f4
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb) &&
ba3a5f4
+	    be32_to_cpu(agf->agf_rmap_blocks) > be32_to_cpu(agf->agf_length))
ba3a5f4
+		return __this_address;
ba3a5f4
+
ba3a5f4
 	/*
ba3a5f4
 	 * during growfs operations, the perag is not fully initialised,
ba3a5f4
 	 * so we can't use it for any useful checking. growfs ensures we can't
ba3a5f4
@@ -2882,6 +2893,11 @@ xfs_agf_verify(
ba3a5f4
 	    be32_to_cpu(agf->agf_btreeblks) > be32_to_cpu(agf->agf_length))
ba3a5f4
 		return __this_address;
ba3a5f4
ba3a5f4
+	if (xfs_sb_version_hasreflink(&mp->m_sb) &&
ba3a5f4
+	    be32_to_cpu(agf->agf_refcount_blocks) >
ba3a5f4
+	    be32_to_cpu(agf->agf_length))
ba3a5f4
+		return __this_address;
ba3a5f4
+
ba3a5f4
 	if (xfs_sb_version_hasreflink(&mp->m_sb) &&
ba3a5f4
 	    (be32_to_cpu(agf->agf_refcount_level) < 1 ||
ba3a5f4
 	     be32_to_cpu(agf->agf_refcount_level) > XFS_BTREE_MAXLEVELS))
ba3a5f4
-- 
ba3a5f4
2.26.2
ba3a5f4