[PATCH 4/6] xfs: automatically fix up AGFL size issues

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux XFS Devel]     [Linux Filesystem Development]     [Filesystem Testing]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux