From: Dave Chinner <dchinner@xxxxxxxxxx> Now that we can safely detect whether we have a screwed up AGFL size, we need to automatically fix it up. We do this by modifying the AGFL index and/or count values in the AGF. This will only ever lead to reducing the size of the AGFL, leaving a free block in the unused slot to remain there if a problem is corrected. WHile this is a leak, it should only occur once and it will be corrected the next time the filesystem is repaired. Signed-off-by: Dave Chinner <dchinner@xxxxxxxxxx> --- fs/xfs/libxfs/xfs_alloc.c | 105 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 6 deletions(-) diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 31a18fe..8deb736 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -2417,10 +2417,7 @@ xfs_agf_verify( if (!(agf->agf_magicnum == cpu_to_be32(XFS_AGF_MAGIC) && XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum)) && - be32_to_cpu(agf->agf_freeblks) <= be32_to_cpu(agf->agf_length) && - be32_to_cpu(agf->agf_flfirst) < agfl_size && - be32_to_cpu(agf->agf_fllast) < agfl_size && - be32_to_cpu(agf->agf_flcount) <= agfl_size)) + be32_to_cpu(agf->agf_freeblks) <= be32_to_cpu(agf->agf_length))) return false; if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS || @@ -2440,10 +2437,18 @@ xfs_agf_verify( be32_to_cpu(agf->agf_btreeblks) > be32_to_cpu(agf->agf_length)) return false; - if (!xfs_sb_version_hascrc(&mp->m_sb)) + /* + * AGFL parameters are strict only for non-CRC filesystems now. + * See the comment below in the v5 format section for details + */ + if (!xfs_sb_version_hascrc(&mp->m_sb)) { + if (!(flfirst < agfl_size && fllast < agfl_size && + flcount <= agfl_size)) + return false; return true; + } - /* CRC format checks only from here */ + /* v5 format checks only from here */ if (!uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid)) return false; @@ -2575,6 +2580,92 @@ xfs_read_agf( } /* + * Detect and fixup a screwup in the struct xfs_agfl definition that results in + * different free list sizes depending on the compiler padding added to the + * xfs_agfl. This will only matter on v5 filesystems that have the freelist + * wrapping around the end of the AGFL. The changes fixup changes will be logged + * in the first free list modification done to the AGF. + */ +static void +xfs_agf_fixup_freelist_count( + struct xfs_mount *mp, + struct xfs_perag *pag, + struct xfs_agf *agf) +{ + int32_t agfl_size = xfs_agfl_size(mp); + int32_t active; + + ASSERT(pag->pagf_fllast <= agfl_size); + ASSERT(pag->pagf_flfirst <= agfl_size); + + if (!xfs_sb_version_hascrc(&mp->m_sb)) + return; + + /* if not wrapped or completely within range, nothing to do */ + if (pag->pagf_fllast < agfl_size && + pag->pagf_flfirst <= pag->pagf_fllast) + return; + + /* if last is invalid, pull it back and return */ + if (pag->pagf_fllast == agfl_size) { + xfs_notice(mp, "AGF %d: last index fixup being performed", + pag->pag_agno); + if (pag->pagf_flcount) { + pag->pagf_flcount--; + be32_add_cpu(&agf->agf_flcount, -1); + be32_add_cpu(&agf->agf_fllast, -1); + pag->pagf_fllast--; + } else { + /* empty free list, move the both pointers back one */ + ASSERT(pag->pagf_flfirst == pag->pagf_fllast); + be32_add_cpu(&agf->agf_fllast, -1); + be32_add_cpu(&agf->agf_flfirst, -1); + pag->pagf_flfirst--; + pag->pagf_fllast--; + } + return; + } + + /* if first is invalid, wrap it, reset count and return */ + if (pag->pagf_flfirst == agfl_size) { + xfs_notice(mp, "AGF %d: first index fixup being performed", + pag->pag_agno); + ASSERT(pag->pagf_flfirst != pag->pagf_fllast); + ASSERT(pag->pagf_flcount); + pag->pagf_flcount = pag->pagf_fllast + 1; + agf->agf_flcount = cpu_to_be32(pag->pagf_flcount); + agf->agf_flfirst = 0; + pag->pagf_flfirst = 0; + return; + } + + /* + * Pure wrap, first and last are valid. + * flfirst + * | + * +oo------------------oo+ + * | + * fllast + * + * We need to determine if the count includes the last slot in the AGFL + * which we no longer use. If the flcount does not match the expected + * size based on the first/last indexes, we need to fix it up. + */ + active = pag->pagf_fllast - pag->pagf_flfirst + 1; + if (active <= 0) + active += agfl_size; + if (active == pag->pagf_flcount) + return; + + /* should only be off by one */ + ASSERT(active + 1 == pag->pagf_flcount); + xfs_notice(mp, "AGF %d: wrapped count fixup being performed", + pag->pag_agno); + pag->pagf_flcount--; + be32_add_cpu(&agf->agf_flcount, -1); +} + +/* * Read in the allocation group header (free/alloc section). */ int /* error */ @@ -2620,6 +2711,8 @@ xfs_alloc_read_agf( pag->pagb_count = 0; pag->pagb_tree = RB_ROOT; pag->pagf_init = 1; + + xfs_agf_fixup_freelist_count(mp, pag, agf); } #ifdef DEBUG else if (!XFS_FORCED_SHUTDOWN(mp)) { -- 2.8.0.rc3 _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs