All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] xfs: sort out the AGFL size mess
@ 2016-09-02  2:27 Dave Chinner
  2016-09-02  2:27 ` [PATCH 1/6] xfs: clean up XFS_AGFL_SIZE Dave Chinner
                   ` (5 more replies)
  0 siblings, 6 replies; 15+ messages in thread
From: Dave Chinner @ 2016-09-02  2:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: xfs

Hi folks,

This patchset attempts to address the overall pproblem with the AGFL
size in the v5 format. The underlying problemis that I screwed up
when defining the AGFL header by not padding it correctly for 32/64
bit system sanity, and so it changed size depending on compiler
padding. This then changes the number of entries in the AGFL, and
that can lead to problems when moving a filesysetm between different
platforms.

What this patchset does is fix the size of the AGFL to be consistent
across all platforms and architectures, and then detects the
off-by-one condition that occurs when a filesystem has a size
mismatch on a wrapped AGFL. It then automatically corrects the
off-by-one so the user does not need to even know that this problem
existed when upgrading their kernel. If there is more than an
off-by-one error, the kernel will flag a corruption in the usual way
(i.e. shutdown) and that is left to repair to fix up.

As the userspace tools always rebuild the AGFL when required, they
do not expose the problematic wrapping condition to the kernel.
Hence we only really need this set of automatic fixups for kernel
upgrade situations. And because we always use the smaller, valid
AGFL size, we can remove the growfs hack we put inplace to prevent
initialising the new AGFL indexes to an invalid index.

I think I've caught all the conditions we need to here. I've been
testing with the script attached below (requires an xfs_db patch I
posted a couple of days ago) to exercise the "detect and correct
oversize AGFL indexes" case. If I run this on an unmodified kernel,
it crashes and burns. With this patch set, it either corrects the
problem automatically or flags corruption. I'll need to turn this
into an xfstest but it suffices for the moment.

Thoughts, comments and testing on random platforms welcome!

-Dave.

--
#!/bin/bash


do_write()
{
	mount /dev/ram0 /mnt/test 
	echo > /mnt/test/foo
	sync
	umount /mnt/test 
	#xfs_db -x -c "agf 0" -c "p" /dev/ram0
	xfs_repair -n /dev/ram0 > /dev/null 2>&1
	xfs_repair /dev/ram0 > /dev/null 2>&1
	mount /dev/ram0 /mnt/test 
	echo > /mnt/test/bar
	umount /mnt/test 
	xfs_repair /dev/ram0 > /dev/null 2>&1
}

agfl_copy()
{
	source=$1
	dest=$2

	agbno=`xfs_db -x -c "agfl 0" -c "p bno[$source]" /dev/ram0 | \
		cut -d "=" -f 2`
	if [ "$agbno" == " null" ]; then
		agbno="0xffffffff"
	fi
	echo agbno "$agbno"
	xfs_db -x -c "agfl 0" -c "write -d bno[$source] 0xffffffff" /dev/ram0 > /dev/null
	xfs_db -x -c "agfl 0" -c "write -d bno[$dest] $agbno" /dev/ram0 > /dev/null
}

run_test()
{
	flfirst=$1
	fllast=$2
	flcount=$3
	urk=$4

	echo "Testing flfirst=$flfirst fllast=$fllast flcount=$flcount...."
	mkfs.xfs -f -s size=512 /dev/ram0 > /dev/null
	xfs_db -x -c "agf 0"                    \
		-c "write -d flfirst $flfirst"  \
		-c "write -d fllast $fllast"    \
		-c "write -d flcount $flcount"  \
		/dev/ram0

	# we need to write a bunch of block numbers into the new part
	# of the AGFL. So we just copy 0 -> flfirst and so on.
	let i=0
	while (($flcount - $i > 0)) ; do
		dst=$((flfirst + i))
		if [ $dst -ge 118 ]; then
			dst=$((dst - 118))
		fi
		agfl_copy $i $dst
		i=$((i + 1))
	done

	do_write
}

# run_test flfirst fllast flcount
#
# mkfs default on 512 byte sectors is "0 3 4" w/ size 118
# hence 118 should be the first invalid index, and the number
# filesystems with the agfl header packing bug use.
#
# We want to test corrections for:
#       flfirst being oversize w/ matching flcount
run_test 118 3 5
#       fllast being oversize w/ matching flcount
run_test 114 118 5
#       flfirst/flast being in range w/ oversize flcount
run_test 117 3 6

#
# We want to test corruption detection for:
# where "non-matching flcount" exercises both too small and too large
#       flfirst being oversize w/ non-matching flcount
run_test 118 3 4
run_test 118 3 6
#       fllast being oversize w/ non-matching flcount
run_test 114 118 4
run_test 114 118 6
#       flfirst/flast being in range w/ non-matching flcount
run_test 117 3 5
run_test 117 3 7

--


_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/6] xfs: clean up XFS_AGFL_SIZE
  2016-09-02  2:27 [RFC PATCH 0/6] xfs: sort out the AGFL size mess Dave Chinner
@ 2016-09-02  2:27 ` Dave Chinner
  2018-01-03 22:27   ` Darrick J. Wong
  2016-09-02  2:27 ` [PATCH 2/6] xfs: shadow agfl indexes in the per-ag structures Dave Chinner
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Dave Chinner @ 2016-09-02  2:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: xfs

From: Dave Chinner <dchinner@redhat.com>

The AGFL size calculation is about to get more complex, so lets turn
the macro into a function first and remove the macro.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/xfs/libxfs/xfs_alloc.c  | 33 ++++++++++++++++++++++++++-------
 fs/xfs/libxfs/xfs_alloc.h  |  2 ++
 fs/xfs/libxfs/xfs_format.h | 13 +------------
 fs/xfs/xfs_fsops.c         |  2 +-
 4 files changed, 30 insertions(+), 20 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 05b5243..23559b9 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -63,6 +63,24 @@ xfs_prealloc_blocks(
 }
 
 /*
+ * Size of the AGFL.  For CRC-enabled filesystes we steal a couple of slots in
+ * the beginning of the block for a proper header with the location information
+ * and CRC.
+ */
+int
+xfs_agfl_size(
+	struct xfs_mount	*mp)
+
+{
+	int			size = mp->m_sb.sb_sectsize;
+
+	if (!xfs_sb_version_hascrc(&mp->m_sb))
+		return size / sizeof(xfs_agblock_t);
+
+	return (size - sizeof(struct xfs_agfl)) / sizeof(xfs_agblock_t);
+}
+
+/*
  * In order to avoid ENOSPC-related deadlock caused by out-of-order locking of
  * AGF buffer (PV 947395), we place constraints on the relationship among
  * actual allocations for data blocks, freelist blocks, and potential file data
@@ -554,7 +572,7 @@ xfs_agfl_verify(
 	if (bp->b_pag && be32_to_cpu(agfl->agfl_seqno) != bp->b_pag->pag_agno)
 		return false;
 
-	for (i = 0; i < XFS_AGFL_SIZE(mp); i++) {
+	for (i = 0; i < xfs_agfl_size(mp); i++) {
 		if (be32_to_cpu(agfl->agfl_bno[i]) != NULLAGBLOCK &&
 		    be32_to_cpu(agfl->agfl_bno[i]) >= mp->m_sb.sb_agblocks)
 			return false;
@@ -2230,7 +2248,7 @@ xfs_alloc_get_freelist(
 	bno = be32_to_cpu(agfl_bno[be32_to_cpu(agf->agf_flfirst)]);
 	be32_add_cpu(&agf->agf_flfirst, 1);
 	xfs_trans_brelse(tp, agflbp);
-	if (be32_to_cpu(agf->agf_flfirst) == XFS_AGFL_SIZE(mp))
+	if (be32_to_cpu(agf->agf_flfirst) == xfs_agfl_size(mp))
 		agf->agf_flfirst = 0;
 
 	pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
@@ -2338,7 +2356,7 @@ xfs_alloc_put_freelist(
 			be32_to_cpu(agf->agf_seqno), &agflbp)))
 		return error;
 	be32_add_cpu(&agf->agf_fllast, 1);
-	if (be32_to_cpu(agf->agf_fllast) == XFS_AGFL_SIZE(mp))
+	if (be32_to_cpu(agf->agf_fllast) == xfs_agfl_size(mp))
 		agf->agf_fllast = 0;
 
 	pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
@@ -2356,7 +2374,7 @@ xfs_alloc_put_freelist(
 
 	xfs_alloc_log_agf(tp, agbp, logflags);
 
-	ASSERT(be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp));
+	ASSERT(be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp));
 
 	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp);
 	blockp = &agfl_bno[be32_to_cpu(agf->agf_fllast)];
@@ -2377,6 +2395,7 @@ xfs_agf_verify(
 	struct xfs_buf	*bp)
  {
 	struct xfs_agf	*agf = XFS_BUF_TO_AGF(bp);
+	int32_t		agfl_size = xfs_agfl_size(mp);
 
 	if (xfs_sb_version_hascrc(&mp->m_sb)) {
 		if (!uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid))
@@ -2389,9 +2408,9 @@ 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) < XFS_AGFL_SIZE(mp) &&
-	      be32_to_cpu(agf->agf_fllast) < XFS_AGFL_SIZE(mp) &&
-	      be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)))
+	      be32_to_cpu(agf->agf_flfirst) < agfl_size &&
+	      be32_to_cpu(agf->agf_fllast) < agfl_size &&
+	      be32_to_cpu(agf->agf_flcount) <= agfl_size))
 		return false;
 
 	if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index 6fe2d6b..cfdd02f 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -202,6 +202,8 @@ xfs_alloc_get_rec(
 
 int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
 			xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
+int xfs_agfl_size(struct xfs_mount *mp);
+
 int xfs_alloc_fix_freelist(struct xfs_alloc_arg *args, int flags);
 int xfs_free_extent_fix_freelist(struct xfs_trans *tp, xfs_agnumber_t agno,
 		struct xfs_buf **agbp);
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 270fb5c..60085f3 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -781,24 +781,13 @@ typedef struct xfs_agi {
 		&(XFS_BUF_TO_AGFL(bp)->agfl_bno[0]) : \
 		(__be32 *)(bp)->b_addr)
 
-/*
- * Size of the AGFL.  For CRC-enabled filesystes we steal a couple of
- * slots in the beginning of the block for a proper header with the
- * location information and CRC.
- */
-#define XFS_AGFL_SIZE(mp) \
-	(((mp)->m_sb.sb_sectsize - \
-	 (xfs_sb_version_hascrc(&((mp)->m_sb)) ? \
-		sizeof(struct xfs_agfl) : 0)) / \
-	  sizeof(xfs_agblock_t))
-
 typedef struct xfs_agfl {
 	__be32		agfl_magicnum;
 	__be32		agfl_seqno;
 	uuid_t		agfl_uuid;
 	__be64		agfl_lsn;
 	__be32		agfl_crc;
-	__be32		agfl_bno[];	/* actually XFS_AGFL_SIZE(mp) */
+	__be32		agfl_bno[];	/* actually xfs_agfl_size(mp) */
 } __attribute__((packed)) xfs_agfl_t;
 
 #define XFS_AGFL_CRC_OFF	offsetof(struct xfs_agfl, agfl_crc)
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 0b7f986..622ca71 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -285,7 +285,7 @@ xfs_growfs_data_private(
 		}
 
 		agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
-		for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++)
+		for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
 			agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
 
 		error = xfs_bwrite(bp);
-- 
2.8.0.rc3

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 2/6] xfs: shadow agfl indexes in the per-ag structures
  2016-09-02  2:27 [RFC PATCH 0/6] xfs: sort out the AGFL size mess Dave Chinner
  2016-09-02  2:27 ` [PATCH 1/6] xfs: clean up XFS_AGFL_SIZE Dave Chinner
@ 2016-09-02  2:27 ` Dave Chinner
  2016-09-02 13:25   ` Eric Sandeen
  2016-09-02  2:27 ` [PATCH 3/6] xfs: detect AGFL size mismatches Dave Chinner
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Dave Chinner @ 2016-09-02  2:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: xfs

From: Dave Chinner <dchinner@redhat.com>

To verify that the AGFL contents is sane, we need to have access to
the indexes that tell us what part of the AGFL is active. We cannot
access the AGF buffer from the AGFL verifier, so we need to shadow
these values in the struct xfs_perag so we check them when required.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/xfs/libxfs/xfs_alloc.c | 4 ++++
 fs/xfs/xfs_mount.h        | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 23559b9..1aef556 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -2255,6 +2255,7 @@ xfs_alloc_get_freelist(
 	be32_add_cpu(&agf->agf_flcount, -1);
 	xfs_trans_agflist_delta(tp, -1);
 	pag->pagf_flcount--;
+	pag->pagf_flfirst = be32_to_cpu(agf->agf_flfirst);
 	xfs_perag_put(pag);
 
 	logflags = XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT;
@@ -2363,6 +2364,7 @@ xfs_alloc_put_freelist(
 	be32_add_cpu(&agf->agf_flcount, 1);
 	xfs_trans_agflist_delta(tp, 1);
 	pag->pagf_flcount++;
+	pag->pagf_fllast = be32_to_cpu(agf->agf_fllast);
 
 	logflags = XFS_AGF_FLLAST | XFS_AGF_FLCOUNT;
 	if (btreeblk) {
@@ -2547,6 +2549,8 @@ xfs_alloc_read_agf(
 		pag->pagf_freeblks = be32_to_cpu(agf->agf_freeblks);
 		pag->pagf_btreeblks = be32_to_cpu(agf->agf_btreeblks);
 		pag->pagf_flcount = be32_to_cpu(agf->agf_flcount);
+		pag->pagf_flfirst = be32_to_cpu(agf->agf_flfirst);
+		pag->pagf_fllast = be32_to_cpu(agf->agf_fllast);
 		pag->pagf_longest = be32_to_cpu(agf->agf_longest);
 		pag->pagf_levels[XFS_BTNUM_BNOi] =
 			be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNOi]);
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index b36676c..3eb1b20 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -340,6 +340,8 @@ typedef struct xfs_perag {
 	__uint8_t	pagf_levels[XFS_BTNUM_AGF];
 					/* # of levels in bno & cnt btree */
 	__uint32_t	pagf_flcount;	/* count of blocks in freelist */
+	__uint32_t	pagf_flfirst;	/* first freelist block's index */
+	__uint32_t	pagf_fllast;	/* last freelist block's index */
 	xfs_extlen_t	pagf_freeblks;	/* total free blocks */
 	xfs_extlen_t	pagf_longest;	/* longest free space */
 	__uint32_t	pagf_btreeblks;	/* # of blocks held in AGF btrees */
-- 
2.8.0.rc3

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 3/6] xfs: detect AGFL size mismatches
  2016-09-02  2:27 [RFC PATCH 0/6] xfs: sort out the AGFL size mess Dave Chinner
  2016-09-02  2:27 ` [PATCH 1/6] xfs: clean up XFS_AGFL_SIZE Dave Chinner
  2016-09-02  2:27 ` [PATCH 2/6] xfs: shadow agfl indexes in the per-ag structures Dave Chinner
@ 2016-09-02  2:27 ` Dave Chinner
  2016-09-13 23:39   ` Eric Sandeen
  2016-09-02  2:27 ` [PATCH 4/6] xfs: automatically fix up AGFL size issues Dave Chinner
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 15+ messages in thread
From: Dave Chinner @ 2016-09-02  2:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: xfs

From: Dave Chinner <dchinner@redhat.com>

In commit 96f859d ("libxfs: pack the agfl header structure so
XFS_AGFL_SIZE is correct"), we changed the definition of the AGFL
header to be packed to fix a problem where the structure changed
size depending on platofrm and compiler.

Unfortunately, this means that there are some filesystems out there
that have a mismatched AGFL on-disk format indexes. We avoided the
obvious problem this causes with commit ad747e3 ("xfs: Don't wrap
growfs AGFL indexes"), but that was really just addressing a symptom
of the problem caused by the changes in the original commit.

We really need to catch this problem on systems where it exists and
correct it.  The first thing we need to do is define what the valid
AGFL indexes are once and for all, and then ensure we can detect
when we have an AGFL that is out of spec purely because of this
issue.

This patch defines the AGFL size for CRC enabled filesystems to be
one entry short of the full AGFL. We chose this configuration
because leaking a block of free space is not the end of the world
and that free block will still be valid if the filesystem is taken
back to an older kernel with wrapped indexes and hence the older
kernel thinks that block is part of the AGFL.

The other approach of growing the AGFL over an index with an
undefined block in it has many obvious problems - the follow-on
effects in the code are quite deep as the freelist is assumed to
always point to known, correct free blocks. Hence it is simpler to
understand and maintain to define the AGFL to take the smaller of
the two formats that we ended up with.

As such, update xfs_agfl_size() appropriately, and add code to the
agf verifier to detect out-of-bounds indexes. This will trigger
corruption warnings and prevent out of bounds accesses occurring
that may lead to silent corruption, but this does not help users
who, unknowingly have this issue and have just upgraded their
kernel.  However, it does now reliably detect the problem and that
is the first step towards automatically correcting it, which will be
done in subsequent patches.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/xfs/libxfs/xfs_alloc.c | 84 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 71 insertions(+), 13 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 1aef556..31a18fe 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -66,6 +66,17 @@ xfs_prealloc_blocks(
  * Size of the AGFL.  For CRC-enabled filesystes we steal a couple of slots in
  * the beginning of the block for a proper header with the location information
  * and CRC.
+ *
+ * Unfortunately, struct xfs_agfl was not cleanly defined to be a consistent
+ * size on different architectures (32 bit gave 36 bytes, 64 bit gave 40 bytes)
+ * and so we've got to take nito account that screwup here.
+ *
+ * We have decide to fix the size of the AGFL at the smaller size as dictated by
+ * 64 bit compilers. To make the calculation consitent across platforms, base it
+ * on the the offset of the agfl_bno field rather than the size of the
+ * structure, and take the extra entry that this results in for all platforms
+ * away from the result. Encoding this into this function allows us to detect
+ * indexes that are out of range when they come off disk.
  */
 int
 xfs_agfl_size(
@@ -77,7 +88,8 @@ xfs_agfl_size(
 	if (!xfs_sb_version_hascrc(&mp->m_sb))
 		return size / sizeof(xfs_agblock_t);
 
-	return (size - sizeof(struct xfs_agfl)) / sizeof(xfs_agblock_t);
+	return ((size - offsetof(struct xfs_agfl, agfl_bno)) /
+						sizeof(xfs_agblock_t)) - 1;
 }
 
 /*
@@ -2398,14 +2410,10 @@ xfs_agf_verify(
  {
 	struct xfs_agf	*agf = XFS_BUF_TO_AGF(bp);
 	int32_t		agfl_size = xfs_agfl_size(mp);
-
-	if (xfs_sb_version_hascrc(&mp->m_sb)) {
-		if (!uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid))
-			return false;
-		if (!xfs_log_check_lsn(mp,
-				be64_to_cpu(XFS_BUF_TO_AGF(bp)->agf_lsn)))
-			return false;
-	}
+	uint32_t	flfirst = be32_to_cpu(agf->agf_flfirst);
+	uint32_t	fllast = be32_to_cpu(agf->agf_fllast);
+	uint32_t	flcount = be32_to_cpu(agf->agf_flcount);
+	int32_t		active;
 
 	if (!(agf->agf_magicnum == cpu_to_be32(XFS_AGF_MAGIC) &&
 	      XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum)) &&
@@ -2419,10 +2427,6 @@ xfs_agf_verify(
 	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) > XFS_BTREE_MAXLEVELS)
 		return false;
 
-	if (xfs_sb_version_hasrmapbt(&mp->m_sb) &&
-	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS)
-		return false;
-
 	/*
 	 * during growfs operations, the perag is not fully initialised,
 	 * so we can't use it for any useful checking. growfs ensures we can't
@@ -2436,6 +2440,60 @@ 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))
+		return true;
+
+	/* CRC format checks only from here */
+
+	if (!uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid))
+		return false;
+	if (!xfs_log_check_lsn(mp, be64_to_cpu(XFS_BUF_TO_AGF(bp)->agf_lsn)))
+		return false;
+
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb) &&
+	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS)
+		return false;
+
+	/*
+	 * Due to a stuff-up with 32/64 bit agfl header structure padding in the
+	 * v5 format, there is a case where the free list uses a slot on 32 bit
+	 * kernels that is not available to 64 bit kernels. In this case, just
+	 * warn on read, knowing that it will be corrected before it is written
+	 * back out. The count will only be out by 1, so any more than this is
+	 * still considered a corruption.
+	 */
+	if (flfirst > agfl_size)
+		return false;
+	if (flfirst == agfl_size)
+		xfs_warn(mp, "AGF %u: first freelist index needs correction",
+			be32_to_cpu(agf->agf_seqno));
+
+	if (fllast > agfl_size)
+		return false;
+	if (fllast == agfl_size)
+		xfs_warn(mp, "AGF %u: last freelist index needs correction",
+			be32_to_cpu(agf->agf_seqno));
+
+	if (flcount > agfl_size + 1)
+		return false;
+	if (flcount == agfl_size)
+		xfs_warn(mp, "AGF %u: freelist count needs correction(1)",
+			be32_to_cpu(agf->agf_seqno));
+
+	/*
+	 * range check flcount - if it's out by more than 1 count or is too
+	 * small, we've got a corruption that isn't a result of the structure
+	 * size screwup so that throws a real corruption error.
+	 */
+	active = fllast - flfirst + 1;
+	if (active <= 0)
+		active += agfl_size;
+	if (flcount > active + 1 || flcount < active)
+		return false;
+	if (flcount != active)
+		xfs_warn(mp, "AGF %u: freelist count needs correction(2)",
+			be32_to_cpu(agf->agf_seqno));
+
 	return true;;
 
 }
-- 
2.8.0.rc3

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 4/6] xfs: automatically fix up AGFL size issues
  2016-09-02  2:27 [RFC PATCH 0/6] xfs: sort out the AGFL size mess Dave Chinner
                   ` (2 preceding siblings ...)
  2016-09-02  2:27 ` [PATCH 3/6] xfs: detect AGFL size mismatches Dave Chinner
@ 2016-09-02  2:27 ` Dave Chinner
  2016-09-14 18:20   ` Darrick J. Wong
  2016-09-02  2:27 ` [PATCH 5/6] xfs: clean up AGFL index initialisation in growfs Dave Chinner
  2016-09-02  2:27 ` [PATCH 6/6] xfs: use per-ag AGFL indexes everywhere Dave Chinner
  5 siblings, 1 reply; 15+ messages in thread
From: Dave Chinner @ 2016-09-02  2:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: xfs

From: Dave Chinner <dchinner@redhat.com>

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@redhat.com>
---
 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@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 5/6] xfs: clean up AGFL index initialisation in growfs
  2016-09-02  2:27 [RFC PATCH 0/6] xfs: sort out the AGFL size mess Dave Chinner
                   ` (3 preceding siblings ...)
  2016-09-02  2:27 ` [PATCH 4/6] xfs: automatically fix up AGFL size issues Dave Chinner
@ 2016-09-02  2:27 ` Dave Chinner
  2016-09-02  2:27 ` [PATCH 6/6] xfs: use per-ag AGFL indexes everywhere Dave Chinner
  5 siblings, 0 replies; 15+ messages in thread
From: Dave Chinner @ 2016-09-02  2:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: xfs

From: Dave Chinner <dchinner@redhat.com>

Now that we have a fixed size for the AGFL for v5 format filesysetms
across all platforms, we don't need the growfs workaround to avoid
using the last index in the AGFL. This effectively undoes commit
ad747e3 ("xfs: Don't wrap growfs AGFL indexes") and returns the
growfs code to it's prior behaviour.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/xfs/xfs_fsops.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 622ca71..e5ff65e 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -251,8 +251,8 @@ xfs_growfs_data_private(
 			agf->agf_rmap_blocks = cpu_to_be32(1);
 		}
 
-		agf->agf_flfirst = cpu_to_be32(1);
-		agf->agf_fllast = 0;
+		agf->agf_flfirst = 0;
+		agf->agf_fllast = cpu_to_be32(xfs_agfl_size(mp) - 1);
 		agf->agf_flcount = 0;
 		tmpsize = agsize - mp->m_ag_prealloc_blocks;
 		agf->agf_freeblks = cpu_to_be32(tmpsize);
-- 
2.8.0.rc3

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 6/6] xfs: use per-ag AGFL indexes everywhere
  2016-09-02  2:27 [RFC PATCH 0/6] xfs: sort out the AGFL size mess Dave Chinner
                   ` (4 preceding siblings ...)
  2016-09-02  2:27 ` [PATCH 5/6] xfs: clean up AGFL index initialisation in growfs Dave Chinner
@ 2016-09-02  2:27 ` Dave Chinner
  5 siblings, 0 replies; 15+ messages in thread
From: Dave Chinner @ 2016-09-02  2:27 UTC (permalink / raw)
  To: linux-xfs; +Cc: xfs

From: Dave Chinner <dchinner@redhat.com>

Now we have the AGFL indexes in the struct xfs_perag, use them
everywhere rather than having to decode them directly from the AGF
buffer. This essentially makes the values in the perag the primary
in-memory copy of the free list state, so we operate on that first
then copy them to the on-disk structure just before logging them.

This removes a lot of endian conversions that are done to manipulate
the on-disk structures, making the frequently called
xfs_alloc_get/put_freelist() substantially more compact and faster.

text	   data	    bss	    dec	    hex	filename
 835459	 157358	   1008	 993825	  f2a21	fs/xfs/xfs.o.old
 835299	 157358	   1008	 993665	  f2981	fs/xfs/xfs.o.new

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/xfs/libxfs/xfs_alloc.c | 135 ++++++++++++++++++++++------------------------
 1 file changed, 64 insertions(+), 71 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 8deb736..554f033 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -2220,66 +2220,62 @@ out_no_agbp:
  * Get a block from the freelist.
  * Returns with the buffer for the block gotten.
  */
-int				/* error */
+int
 xfs_alloc_get_freelist(
-	xfs_trans_t	*tp,	/* transaction pointer */
-	xfs_buf_t	*agbp,	/* buffer containing the agf structure */
-	xfs_agblock_t	*bnop,	/* block address retrieved from freelist */
+	struct xfs_trans *tp,
+	struct xfs_buf	*agbp,
+	xfs_agblock_t	*bnop,
 	int		btreeblk) /* destination is a AGF btree */
 {
-	xfs_agf_t	*agf;	/* a.g. freespace structure */
-	xfs_buf_t	*agflbp;/* buffer for a.g. freelist structure */
-	xfs_agblock_t	bno;	/* block number returned */
+	struct xfs_mount *mp = tp->t_mountp;
+	struct xfs_agf	*agf = XFS_BUF_TO_AGF(agbp);
+	struct xfs_buf	*agflbp;
+	struct xfs_perag *pag;
+	xfs_agnumber_t	agno = be32_to_cpu(agf->agf_seqno);
+	xfs_agblock_t	bno = NULLAGBLOCK;
 	__be32		*agfl_bno;
 	int		error;
 	int		logflags;
-	xfs_mount_t	*mp = tp->t_mountp;
-	xfs_perag_t	*pag;	/* per allocation group data */
 
-	/*
-	 * Freelist is empty, give up.
-	 */
-	agf = XFS_BUF_TO_AGF(agbp);
-	if (!agf->agf_flcount) {
-		*bnop = NULLAGBLOCK;
-		return 0;
-	}
+	/* Freelist is empty, give up. */
+	pag = xfs_perag_get(mp, agno);
+	if (!pag->pagf_flcount)
+		goto out_put_perag;
+
 	/*
 	 * Read the array of free blocks.
 	 */
-	error = xfs_alloc_read_agfl(mp, tp, be32_to_cpu(agf->agf_seqno),
-				    &agflbp);
+	error = xfs_alloc_read_agfl(mp, tp, agno, &agflbp);
 	if (error)
 		return error;
 
+	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp);
+	logflags = XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT;
 
 	/*
 	 * Get the block number and update the data structures.
 	 */
-	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp);
-	bno = be32_to_cpu(agfl_bno[be32_to_cpu(agf->agf_flfirst)]);
-	be32_add_cpu(&agf->agf_flfirst, 1);
-	xfs_trans_brelse(tp, agflbp);
-	if (be32_to_cpu(agf->agf_flfirst) == xfs_agfl_size(mp))
-		agf->agf_flfirst = 0;
-
-	pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
-	be32_add_cpu(&agf->agf_flcount, -1);
-	xfs_trans_agflist_delta(tp, -1);
+	bno = be32_to_cpu(agfl_bno[pag->pagf_flfirst]);
+	if (++pag->pagf_flfirst == xfs_agfl_size(mp))
+		pag->pagf_flfirst = 0;
 	pag->pagf_flcount--;
-	pag->pagf_flfirst = be32_to_cpu(agf->agf_flfirst);
-	xfs_perag_put(pag);
+	xfs_trans_agflist_delta(tp, -1);
 
-	logflags = XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT;
 	if (btreeblk) {
-		be32_add_cpu(&agf->agf_btreeblks, 1);
 		pag->pagf_btreeblks++;
+		agf->agf_btreeblks = cpu_to_be32(pag->pagf_btreeblks);
 		logflags |= XFS_AGF_BTREEBLKS;
 	}
 
+	agf->agf_flfirst = cpu_to_be32(pag->pagf_flfirst);
+	agf->agf_flcount = cpu_to_be32(pag->pagf_flcount);
+
+	xfs_trans_brelse(tp, agflbp);
 	xfs_alloc_log_agf(tp, agbp, logflags);
-	*bnop = bno;
 
+out_put_perag:
+	xfs_perag_put(pag);
+	*bnop = bno;
 	return 0;
 }
 
@@ -2345,53 +2341,51 @@ xfs_alloc_pagf_init(
 /*
  * Put the block on the freelist for the allocation group.
  */
-int					/* error */
+int
 xfs_alloc_put_freelist(
-	xfs_trans_t		*tp,	/* transaction pointer */
-	xfs_buf_t		*agbp,	/* buffer for a.g. freelist header */
-	xfs_buf_t		*agflbp,/* buffer for a.g. free block array */
-	xfs_agblock_t		bno,	/* block being freed */
+	struct xfs_trans	*tp,
+	struct xfs_buf		*agbp,
+	struct xfs_buf		*agflbp,
+	xfs_agblock_t		bno,
 	int			btreeblk) /* block came from a AGF btree */
 {
-	xfs_agf_t		*agf;	/* a.g. freespace structure */
-	__be32			*blockp;/* pointer to array entry */
+	struct xfs_mount	*mp = tp->t_mountp;
+	struct xfs_agf		*agf = XFS_BUF_TO_AGF(agbp);
+	struct xfs_perag	*pag;
+	xfs_agnumber_t		agno = be32_to_cpu(agf->agf_seqno);
+	__be32			*agfl_bno;
+	__be32			*blockp;
 	int			error;
 	int			logflags;
-	xfs_mount_t		*mp;	/* mount structure */
-	xfs_perag_t		*pag;	/* per allocation group data */
-	__be32			*agfl_bno;
 	int			startoff;
 
-	agf = XFS_BUF_TO_AGF(agbp);
-	mp = tp->t_mountp;
+	if (!agflbp) {
+		error = xfs_alloc_read_agfl(mp, tp, agno, &agflbp);
+		if (error)
+			return error;
+	}
 
-	if (!agflbp && (error = xfs_alloc_read_agfl(mp, tp,
-			be32_to_cpu(agf->agf_seqno), &agflbp)))
-		return error;
-	be32_add_cpu(&agf->agf_fllast, 1);
-	if (be32_to_cpu(agf->agf_fllast) == xfs_agfl_size(mp))
-		agf->agf_fllast = 0;
+	logflags = XFS_AGF_FLLAST | XFS_AGF_FLCOUNT;
 
-	pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
-	be32_add_cpu(&agf->agf_flcount, 1);
-	xfs_trans_agflist_delta(tp, 1);
+	pag = xfs_perag_get(mp, agno);
 	pag->pagf_flcount++;
-	pag->pagf_fllast = be32_to_cpu(agf->agf_fllast);
+	ASSERT(pag->pagf_flcount <= xfs_agfl_size(mp));
 
-	logflags = XFS_AGF_FLLAST | XFS_AGF_FLCOUNT;
+	if (++pag->pagf_fllast == xfs_agfl_size(mp))
+		pag->pagf_fllast = 0;
+	xfs_trans_agflist_delta(tp, 1);
 	if (btreeblk) {
-		be32_add_cpu(&agf->agf_btreeblks, -1);
 		pag->pagf_btreeblks--;
+		agf->agf_btreeblks = cpu_to_be32(pag->pagf_btreeblks);
 		logflags |= XFS_AGF_BTREEBLKS;
 	}
-	xfs_perag_put(pag);
 
-	xfs_alloc_log_agf(tp, agbp, logflags);
+	agf->agf_fllast = cpu_to_be32(pag->pagf_fllast);
+	agf->agf_flcount = cpu_to_be32(pag->pagf_flcount);
 
-	ASSERT(be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp));
 
 	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp);
-	blockp = &agfl_bno[be32_to_cpu(agf->agf_fllast)];
+	blockp = &agfl_bno[pag->pagf_fllast];
 	*blockp = cpu_to_be32(bno);
 	startoff = (char *)blockp - (char *)agflbp->b_addr;
 
@@ -2400,6 +2394,7 @@ xfs_alloc_put_freelist(
 	xfs_trans_buf_set_type(tp, agflbp, XFS_BLFT_AGFL_BUF);
 	xfs_trans_log_buf(tp, agflbp, startoff,
 			  startoff + sizeof(xfs_agblock_t) - 1);
+	xfs_perag_put(pag);
 	return 0;
 }
 
@@ -2612,18 +2607,14 @@ xfs_agf_fixup_freelist_count(
 			   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;
+		goto out_update_agf;
 	}
 
 	/* if first is invalid, wrap it, reset count and return */
@@ -2633,10 +2624,8 @@ xfs_agf_fixup_freelist_count(
 		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;
+		goto out_update_agf;
 	}
 
 	/*
@@ -2662,7 +2651,11 @@ xfs_agf_fixup_freelist_count(
 	xfs_notice(mp, "AGF %d: wrapped count fixup being performed",
 		   pag->pag_agno);
 	pag->pagf_flcount--;
-	be32_add_cpu(&agf->agf_flcount, -1);
+
+out_update_agf:
+	agf->agf_flfirst = cpu_to_be32(pag->pagf_flfirst);
+	agf->agf_fllast = cpu_to_be32(pag->pagf_fllast);
+	agf->agf_flcount = cpu_to_be32(pag->pagf_flcount);
 }
 
 /*
-- 
2.8.0.rc3

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/6] xfs: shadow agfl indexes in the per-ag structures
  2016-09-02  2:27 ` [PATCH 2/6] xfs: shadow agfl indexes in the per-ag structures Dave Chinner
@ 2016-09-02 13:25   ` Eric Sandeen
  2016-09-02 23:06     ` Dave Chinner
  0 siblings, 1 reply; 15+ messages in thread
From: Eric Sandeen @ 2016-09-02 13:25 UTC (permalink / raw)
  To: xfs

On 9/1/16 9:27 PM, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> To verify that the AGFL contents is sane, we need to have access to
> the indexes that tell us what part of the AGFL is active. We cannot
> access the AGF buffer from the AGFL verifier, so we need to shadow
> these values in the struct xfs_perag so we check them when required.
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> ---
>  fs/xfs/libxfs/xfs_alloc.c | 4 ++++
>  fs/xfs/xfs_mount.h        | 2 ++
>  2 files changed, 6 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
> index 23559b9..1aef556 100644
> --- a/fs/xfs/libxfs/xfs_alloc.c
> +++ b/fs/xfs/libxfs/xfs_alloc.c
> @@ -2255,6 +2255,7 @@ xfs_alloc_get_freelist(
>  	be32_add_cpu(&agf->agf_flcount, -1);
>  	xfs_trans_agflist_delta(tp, -1);
>  	pag->pagf_flcount--;
> +	pag->pagf_flfirst = be32_to_cpu(agf->agf_flfirst);
>  	xfs_perag_put(pag);
>  
>  	logflags = XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT;

Still reviewing, but this kind of jumped out at me, seems like the get/put
functions are a bit jumbled up:

        /*
         * Get the block number and update the data structures.
         */
        agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp);
        bno = be32_to_cpu(agfl_bno[be32_to_cpu(agf->agf_flfirst)]);
        be32_add_cpu(&agf->agf_flfirst, 1);
        xfs_trans_brelse(tp, agflbp);
        if (be32_to_cpu(agf->agf_flfirst) == XFS_AGFL_SIZE(mp))
                agf->agf_flfirst = 0;

        pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
        be32_add_cpu(&agf->agf_flcount, -1);
        xfs_trans_agflist_delta(tp, -1);
        pag->pagf_flcount--;
        xfs_perag_put(pag);


why is there a trans_brelse in between two lines which handle proper setting
of flfirst?  I was looking at this thinking about where the pag structures
get updated, then saw the kind of swiss-cheese placement of the first/count
manipulation...

If pagf_flcount/agf_flcount, pagf_flfirst/agf_flfirst etc need to stay in
sync, should there be a wrapper to encapsulate it all?

like:

xfs_agf_{advance/drop/remove}_first(mp, agf, pagf)
{
        be32_add_cpu(&agf->agf_flfirst, 1);
        if (be32_to_cpu(agf->agf_flfirst) == XFS_AGFL_SIZE(mp))
                agf->agf_flfirst = 0;
	pag->pagf_flfirst = be32_to_cpu(agf->agf_flfirst);
        be32_add_cpu(&agf->agf_flcount, -1);
        pag->pagf_flcount--;
}

or something similar?

-Eric

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 2/6] xfs: shadow agfl indexes in the per-ag structures
  2016-09-02 13:25   ` Eric Sandeen
@ 2016-09-02 23:06     ` Dave Chinner
  0 siblings, 0 replies; 15+ messages in thread
From: Dave Chinner @ 2016-09-02 23:06 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: xfs

On Fri, Sep 02, 2016 at 08:25:12AM -0500, Eric Sandeen wrote:
> On 9/1/16 9:27 PM, Dave Chinner wrote:
> > From: Dave Chinner <dchinner@redhat.com>
> > 
> > To verify that the AGFL contents is sane, we need to have access to
> > the indexes that tell us what part of the AGFL is active. We cannot
> > access the AGF buffer from the AGFL verifier, so we need to shadow
> > these values in the struct xfs_perag so we check them when required.
> > 
> > Signed-off-by: Dave Chinner <dchinner@redhat.com>
> > ---
> >  fs/xfs/libxfs/xfs_alloc.c | 4 ++++
> >  fs/xfs/xfs_mount.h        | 2 ++
> >  2 files changed, 6 insertions(+)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
> > index 23559b9..1aef556 100644
> > --- a/fs/xfs/libxfs/xfs_alloc.c
> > +++ b/fs/xfs/libxfs/xfs_alloc.c
> > @@ -2255,6 +2255,7 @@ xfs_alloc_get_freelist(
> >  	be32_add_cpu(&agf->agf_flcount, -1);
> >  	xfs_trans_agflist_delta(tp, -1);
> >  	pag->pagf_flcount--;
> > +	pag->pagf_flfirst = be32_to_cpu(agf->agf_flfirst);
> >  	xfs_perag_put(pag);
> >  
> >  	logflags = XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT;
> 
> Still reviewing, but this kind of jumped out at me, seems like the get/put
> functions are a bit jumbled up:

Gets cleaned up in a later patch.

> sync, should there be a wrapper to encapsulate it all?
> 
> like:
> 
> xfs_agf_{advance/drop/remove}_first(mp, agf, pagf)

Not really necessary for single use functions whose express purpose
is manipulating the agfl indexes

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/6] xfs: detect AGFL size mismatches
  2016-09-02  2:27 ` [PATCH 3/6] xfs: detect AGFL size mismatches Dave Chinner
@ 2016-09-13 23:39   ` Eric Sandeen
  2016-09-14 21:26     ` Dave Chinner
  0 siblings, 1 reply; 15+ messages in thread
From: Eric Sandeen @ 2016-09-13 23:39 UTC (permalink / raw)
  To: xfs

On 9/1/16 9:27 PM, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> In commit 96f859d ("libxfs: pack the agfl header structure so
> XFS_AGFL_SIZE is correct"), we changed the definition of the AGFL
> header to be packed to fix a problem where the structure changed
> size depending on platofrm and compiler.
> 
> Unfortunately, this means that there are some filesystems out there
> that have a mismatched AGFL on-disk format indexes. We avoided the
> obvious problem this causes with commit ad747e3 ("xfs: Don't wrap
> growfs AGFL indexes"), but that was really just addressing a symptom
> of the problem caused by the changes in the original commit.
> 
> We really need to catch this problem on systems where it exists and
> correct it.  The first thing we need to do is define what the valid
> AGFL indexes are once and for all, and then ensure we can detect
> when we have an AGFL that is out of spec purely because of this
> issue.
> 
> This patch defines the AGFL size for CRC enabled filesystems to be
> one entry short of the full AGFL. We chose this configuration
> because leaking a block of free space is not the end of the world
> and that free block will still be valid if the filesystem is taken
> back to an older kernel with wrapped indexes and hence the older
> kernel thinks that block is part of the AGFL.
> 
> The other approach of growing the AGFL over an index with an
> undefined block in it has many obvious problems - the follow-on
> effects in the code are quite deep as the freelist is assumed to
> always point to known, correct free blocks. Hence it is simpler to
> understand and maintain to define the AGFL to take the smaller of
> the two formats that we ended up with.
> 
> As such, update xfs_agfl_size() appropriately, and add code to the
> agf verifier to detect out-of-bounds indexes. This will trigger
> corruption warnings and prevent out of bounds accesses occurring
> that may lead to silent corruption, but this does not help users
> who, unknowingly have this issue and have just upgraded their
> kernel.  However, it does now reliably detect the problem and that
> is the first step towards automatically correcting it, which will be
> done in subsequent patches.

Review below, with notes to myself, pardon the rambling.  One issue,
though, I think, along with nitpicks.

> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> ---
>  fs/xfs/libxfs/xfs_alloc.c | 84 +++++++++++++++++++++++++++++++++++++++--------
>  1 file changed, 71 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
> index 1aef556..31a18fe 100644
> --- a/fs/xfs/libxfs/xfs_alloc.c
> +++ b/fs/xfs/libxfs/xfs_alloc.c
> @@ -66,6 +66,17 @@ xfs_prealloc_blocks(
>   * Size of the AGFL.  For CRC-enabled filesystes we steal a couple of slots in
>   * the beginning of the block for a proper header with the location information
>   * and CRC.
> + *
> + * Unfortunately, struct xfs_agfl was not cleanly defined to be a consistent
> + * size on different architectures (32 bit gave 36 bytes, 64 bit gave 40 bytes)
> + * and so we've got to take nito account that screwup here.

into

> + *
> + * We have decide to fix the size of the AGFL at the smaller size as dictated by

decided

> + * 64 bit compilers. To make the calculation consitent across platforms, base it

consistent ;)

> + * on the the offset of the agfl_bno field rather than the size of the
> + * structure, and take the extra entry that this results in for all platforms
> + * away from the result. Encoding this into this function allows us to detect
> + * indexes that are out of range when they come off disk.
>   */
>  int
>  xfs_agfl_size(
> @@ -77,7 +88,8 @@ xfs_agfl_size(
>  	if (!xfs_sb_version_hascrc(&mp->m_sb))
>  		return size / sizeof(xfs_agblock_t);
>  
> -	return (size - sizeof(struct xfs_agfl)) / sizeof(xfs_agblock_t);
> +	return ((size - offsetof(struct xfs_agfl, agfl_bno)) /
> +						sizeof(xfs_agblock_t)) - 1;
>  }

ok, makes sense.  offsetof makes sure we don't count any padding.
-1 takes away one slot.

>  /*
> @@ -2398,14 +2410,10 @@ xfs_agf_verify(
>   {
>  	struct xfs_agf	*agf = XFS_BUF_TO_AGF(bp);
>  	int32_t		agfl_size = xfs_agfl_size(mp);
> -
> -	if (xfs_sb_version_hascrc(&mp->m_sb)) {
> -		if (!uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid))
> -			return false;
> -		if (!xfs_log_check_lsn(mp,
> -				be64_to_cpu(XFS_BUF_TO_AGF(bp)->agf_lsn)))
> -			return false;
> -	}

yup moving that down is nice.

> +	uint32_t	flfirst = be32_to_cpu(agf->agf_flfirst);
> +	uint32_t	fllast = be32_to_cpu(agf->agf_fllast);
> +	uint32_t	flcount = be32_to_cpu(agf->agf_flcount);
> +	int32_t		active;
>  
>  	if (!(agf->agf_magicnum == cpu_to_be32(XFS_AGF_MAGIC) &&
>  	      XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum)) &&
> @@ -2419,10 +2427,6 @@ xfs_agf_verify(
>  	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) > XFS_BTREE_MAXLEVELS)
>  		return false;
>  
> -	if (xfs_sb_version_hasrmapbt(&mp->m_sb) &&
> -	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS)
> -		return false;
> -
>  	/*
>  	 * during growfs operations, the perag is not fully initialised,
>  	 * so we can't use it for any useful checking. growfs ensures we can't
> @@ -2436,6 +2440,60 @@ 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))
> +		return true;
> +
> +	/* CRC format checks only from here */
> +
> +	if (!uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid))
> +		return false;
> +	if (!xfs_log_check_lsn(mp, be64_to_cpu(XFS_BUF_TO_AGF(bp)->agf_lsn)))
> +		return false;
> +
> +	if (xfs_sb_version_hasrmapbt(&mp->m_sb) &&
> +	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]) > XFS_BTREE_MAXLEVELS)
> +		return false;

/// end of original, moved checks

> +	/*
> +	 * Due to a stuff-up with 32/64 bit agfl header structure padding in the
> +	 * v5 format, there is a case where the free list uses a slot on 32 bit
> +	 * kernels that is not available to 64 bit kernels.

Ok right, *agfl* header is bigger on 64-bit, leaving *fewer* slots
on 64-bit.

> In this case, just
> +	 * warn on read, knowing that it will be corrected before it is written
> +	 * back out. The count will only be out by 1, so any more than this is

"off by one?"

> +	 * still considered a corruption.
> +	 */
> +	if (flfirst > agfl_size)
> +		return false;
> +	if (flfirst == agfl_size)
> +		xfs_warn(mp, "AGF %u: first freelist index needs correction",
> +			be32_to_cpu(agf->agf_seqno));
> +
> +	if (fllast > agfl_size)
> +		return false;
> +	if (fllast == agfl_size)
> +		xfs_warn(mp, "AGF %u: last freelist index needs correction",
> +			be32_to_cpu(agf->agf_seqno));
> +
> +	if (flcount > agfl_size + 1)
> +		return false;
> +	if (flcount == agfl_size)
> +		xfs_warn(mp, "AGF %u: freelist count needs correction(1)",
> +			be32_to_cpu(agf->agf_seqno));

<thinking cap>

Hang on, doesn't this miss the flcount == agfl_size + 1 case in between?

agfl size is now one shorter than expected, but it's still our working
maximum size for validity.  So flcount == agfl_size should be *ok* right?
flcount == agfl_size + 1 needs fixing.  flcount > agfl_size is baroque.

So I'd have expected:

+	if (flcount > agfl_size + 1)
+		return false;
+	if (flcount == agfl_size + 1)
+		xfs_warn(mp, "AGF %u: freelist count needs correction(1)",
+			be32_to_cpu(agf->agf_seqno));

> +
> +	/*
> +	 * range check flcount - if it's out by more than 1 count or is too

ok maybe "out by" is just what you say down there.  ;)

> +	 * small, we've got a corruption that isn't a result of the structure
> +	 * size screwup so that throws a real corruption error.
> +	 */
> +	active = fllast - flfirst + 1;

<thinking cap>
Ok, flfirst or fllast *could* be occupying the "extra" slot, as warned
above.  So active could be "1 bigger" than agfl_size

> +	if (active <= 0)
> +		active += agfl_size;

and here we handle the wrap w/ that smaller agfl_size.  Hm...

Pretend agfl_size is 10 (slots 0->9).  Actual size is possibly 11 (0->10).
We could have flfirst at 10, fllast at 0.  fllast - flfirst + 1 then
is -9.  We add back in agfl_size of 10, and get active == 1.

But with flfirst != fllast we have at least 2 active, right?

So - do you need to keep some "extra slot used" state from above to handle
the wrap correctly when calculating active items?

Going forward I'll pretend that active is indeed the correct number...

> +	if (flcount > active + 1 || flcount < active)
> +		return false;

<implicit: else if flcount == active it's all ok, else - >

> +	if (flcount != active)
> +		xfs_warn(mp, "AGF %u: freelist count needs correction(2)",
> +			be32_to_cpu(agf->agf_seqno));
> +
>  	return true;;

so I think this part is ok ...

-Eric

>  }
> 

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 4/6] xfs: automatically fix up AGFL size issues
  2016-09-02  2:27 ` [PATCH 4/6] xfs: automatically fix up AGFL size issues Dave Chinner
@ 2016-09-14 18:20   ` Darrick J. Wong
  2016-09-14 21:50     ` Dave Chinner
  0 siblings, 1 reply; 15+ messages in thread
From: Darrick J. Wong @ 2016-09-14 18:20 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs, xfs

On Fri, Sep 02, 2016 at 12:27:35PM +1000, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> 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@redhat.com>
> ---
>  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);

I've been wondering whether we need to take the extra step of clearing
that last slot in the AGFL.  Prior to 4.5, 32-bit kernels thought
XFS_AGFL_SIZE was 119 (4k blocks, 512b sectors) and 64-bit kernels
thought it was 118.  Since then, both 32b and 64b think it's 119; with
this patchset we're making it 118 everywhere.  My initial fear was that
the following could happen:

1. Mount with an agfl-119 kernel, beat on the fs until the agfl wraps
around the end.  The last agfl pointer is set to something.

2. Remount with a patched agfl-118 kernel that makes this correction.
The last agfl pointer remains set to whatever.  Exercise the fs until
the agfl active list wraps around again.

3. Remount with the old agfl-119 kernel.  It is now working with flcount
values that don't add up in its worldview, but will it notice?  In any
case, it will end up using that last agfl pointer.  Can we guarantee
that block is not owned by something else?

I /think/ the answer to #2 is that a block only ends up on the AGFL
after it's been removed from the freespace btrees, so the block pointed
to in that last slot is still free and in fact can be used.  Therefore,
the patch is correct and we don't need to write NULLAGBLOCK to the that
last AGFL slot that we're never going to use again, and I'm worrying
about nothing.

xfs_repair writes 0xFF to the entire sector, rebuilds the freesp btrees,
and moves the agfl to the start of the sector, so we're covered for that
case.

As for the question of whether or not an old kernel will notice flcount
not fitting its world view w.r.t. fllast - flfirst + 1, I don't know if
old kernels will notice; the current verifiers don't seem to check.
If we wanted to be really heavy handed I suppose we could set that last
slot to sb_agblocks to stop all the agfl-119 kernels dead in their
tracks, but I don't know that's necessary.

--D

> +}
> +
> +/*
>   * 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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/6] xfs: detect AGFL size mismatches
  2016-09-13 23:39   ` Eric Sandeen
@ 2016-09-14 21:26     ` Dave Chinner
  2016-09-28 17:39       ` Eric Sandeen
  0 siblings, 1 reply; 15+ messages in thread
From: Dave Chinner @ 2016-09-14 21:26 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: xfs

On Tue, Sep 13, 2016 at 06:39:23PM -0500, Eric Sandeen wrote:
> On 9/1/16 9:27 PM, Dave Chinner wrote:
> > +	 * still considered a corruption.
> > +	 */
> > +	if (flfirst > agfl_size)
> > +		return false;
> > +	if (flfirst == agfl_size)
> > +		xfs_warn(mp, "AGF %u: first freelist index needs correction",
> > +			be32_to_cpu(agf->agf_seqno));
> > +
> > +	if (fllast > agfl_size)
> > +		return false;
> > +	if (fllast == agfl_size)
> > +		xfs_warn(mp, "AGF %u: last freelist index needs correction",
> > +			be32_to_cpu(agf->agf_seqno));
> > +
> > +	if (flcount > agfl_size + 1)
> > +		return false;
> > +	if (flcount == agfl_size)
> > +		xfs_warn(mp, "AGF %u: freelist count needs correction(1)",
> > +			be32_to_cpu(agf->agf_seqno));
> 
> <thinking cap>
> 
> Hang on, doesn't this miss the flcount == agfl_size + 1 case in between?
> 
> agfl size is now one shorter than expected, but it's still our working
> maximum size for validity.  So flcount == agfl_size should be *ok* right?
> flcount == agfl_size + 1 needs fixing.  flcount > agfl_size is baroque.
> 
> So I'd have expected:
> 
> +	if (flcount > agfl_size + 1)
> +		return false;
> +	if (flcount == agfl_size + 1)
> +		xfs_warn(mp, "AGF %u: freelist count needs correction(1)",
> +			be32_to_cpu(agf->agf_seqno));
> 
> > +
> > +	/*
> > +	 * range check flcount - if it's out by more than 1 count or is too
> 
> ok maybe "out by" is just what you say down there.  ;)
> 
> > +	 * small, we've got a corruption that isn't a result of the structure
> > +	 * size screwup so that throws a real corruption error.
> > +	 */
> > +	active = fllast - flfirst + 1;
> 
> <thinking cap>
> Ok, flfirst or fllast *could* be occupying the "extra" slot, as warned
> above.  So active could be "1 bigger" than agfl_size
> 
> > +	if (active <= 0)
> > +		active += agfl_size;
> 
> and here we handle the wrap w/ that smaller agfl_size.  Hm...
> 
> Pretend agfl_size is 10 (slots 0->9).  Actual size is possibly 11 (0->10).
> We could have flfirst at 10, fllast at 0.
> fllast - flfirst + 1 then
> is -9.  We add back in agfl_size of 10, and get active == 1.

The free list indexes, as I've said before, are /very badly named/.
The actual situation when flfirst > fllast is this (taken from the
next patch):

        /*
         * 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.
         */

i.e. flfirst is the /tail/ of the list where we allocate blocks from,
fllast is the /head/ of the list where we put newly freed blocks.

hence if we have agfl_size = 10, flfirst = 9, flast = 0, we have 2
blocks on the freelist. i.e. flcount = 2.

good wrap case w/ corrected agfl_size:

	flfirst = 9
	fllast = 0
	flcount = 10
	agfl_size = 10
	active = fllast - flfirst + 1
	       = 0 - 9 + 1
	       = -8 + agflsize
	       = 2 (correct!)

	if (flfirst > agfl_size)		not true
	if (flfirst == agfl_size)		not true

	if (fllast > agfl_size)			not true
	if (fllast == agfl_size)		not true

	if (flcount > agfl_size + 1)		not true
	if (flcount == agfl_size)		not true

	if (active <= 0)			not true
	if (flcount > active + 1)		not true
	if (flcount < active)			not true
	if (flcount != active)			not true

No errors, no warnings, all good!

Bad wrap case:

	flfirst = 10	(invalid!)
	fllast = 0
	flcount = 2
	agfl_size = 10
	active = 0 - 10 + 1
	       = -9 + agfl_size
	       = 1 (correct as flfirst is invalid!)

	if (flfirst > agfl_size)		not true
	if (flfirst == agfl_size)		true, warn

	if (fllast > agfl_size)			not true
	if (fllast == agfl_size)		not true

	if (flcount > agfl_size + 1)		not true
	if (flcount == agfl_size)		not true

	if (active <= 0)			not true
	if (flcount > active + 1)		not true
	if (flcount < active)			not true
	if (flcount != active)			true, warn

So, two correction needed warnings, which is correct because both
flfirst and flcount need fixing due to being off by one.

The bad fllast case:

	flfirst = 5
	fllast = 10	(invalid!)
	flcount = 6	
	agfl_size = 10
	active = fllast - flfirst + 1
	       = 10 - 5 + 1
	       = 6 (invalid as fllast is out of range)

	if (flfirst > agfl_size)		not true
	if (flfirst == agfl_size)		not true

	if (fllast > agfl_size)			not true
	if (fllast == agfl_size)		true, warn

	if (flcount > agfl_size + 1)		not true
	if (flcount == agfl_size)		not true

	if (active <= 0)			not true
	if (flcount > active + 1)		not true
	if (flcount < active)			not true
	if (flcount != active)			not true

So we've got a warning that a fllast correction will take place,
which will also correct the flcount once the fllast index is
updated.

But I think the case you are concerned about is a corner case of
this one - a full AGFL, right?  Something that, in practice, will
never happen as no single transaction will free or require a full
AGFL worth of blocks in a single allocation/free operation.

As such, I didn't actually consider it or test that case, so, it may
be wrong.  Let's run the numbers:

	flfirst = 0
	fllast = 10	(invalid!)
	flcount = 11	(invalid!)
	agfl_size = 10
	active = fllast - flfirst + 1
	       = 10 - 0 + 1
	       = 11 (invalid as fllast is out of range)

	if (flfirst > agfl_size)		not true
	if (flfirst == agfl_size)		not true

	if (fllast > agfl_size)			not true
	if (fllast == agfl_size)		true, warn

	if (flcount > agfl_size + 1)		not true
	if (flcount == agfl_size)		not true

	if (active <= 0)			not true
	if (flcount > active + 1)		not true
	if (flcount < active)			not true
	if (flcount != active)			not true

So it issues the same fllast warning as the not full, not wrapped
case, and that correction also fixes up the flcount. Maybe it's the
full wrapped case, which may be a pure flcount error?

	flfirst = 5
	fllast = 4
	flcount = 11	(invalid!)
	agfl_size = 10
	active = fllast - flfirst + 1
	       = 4 - 5 + 1
	       = 10 (different to flcount!)

	if (flfirst > agfl_size)		not true
	if (flfirst == agfl_size)		not true

	if (fllast > agfl_size)			not true
	if (fllast == agfl_size)		not true

	if (flcount > agfl_size + 1)		not true
	if (flcount == agfl_size)		not true
					(yes, that should warn)

	if (active <= 0)			not true
	if (flcount > active + 1)		not true
	if (flcount < active)			not true
	if (flcount != active)			true, warn

So we do get a warning about the flcount being wrong because it's
only one block different to the calculated active size (and hence it
will be corrected), but no warning from the flcount being out of
range by one block. So, yes, I think you are right, Eric, that the
check should be against agfl_size + 1, even it it means we get
multiple warnings...

Thanks for thinking about this case!

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 4/6] xfs: automatically fix up AGFL size issues
  2016-09-14 18:20   ` Darrick J. Wong
@ 2016-09-14 21:50     ` Dave Chinner
  0 siblings, 0 replies; 15+ messages in thread
From: Dave Chinner @ 2016-09-14 21:50 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs, xfs

On Wed, Sep 14, 2016 at 11:20:44AM -0700, Darrick J. Wong wrote:
> On Fri, Sep 02, 2016 at 12:27:35PM +1000, Dave Chinner wrote:
> > From: Dave Chinner <dchinner@redhat.com>
> I've been wondering whether we need to take the extra step of clearing
> that last slot in the AGFL.  Prior to 4.5, 32-bit kernels thought
> XFS_AGFL_SIZE was 119 (4k blocks, 512b sectors) and 64-bit kernels
> thought it was 118.  Since then, both 32b and 64b think it's 119; with
> this patchset we're making it 118 everywhere.  My initial fear was that
> the following could happen:
> 
> 1. Mount with an agfl-119 kernel, beat on the fs until the agfl wraps
> around the end.  The last agfl pointer is set to something.
> 
> 2. Remount with a patched agfl-118 kernel that makes this correction.
> The last agfl pointer remains set to whatever.  Exercise the fs until
> the agfl active list wraps around again.
> 
> 3. Remount with the old agfl-119 kernel.  It is now working with flcount
> values that don't add up in its worldview, but will it notice?  In any
> case, it will end up using that last agfl pointer.  Can we guarantee
> that block is not owned by something else?

Yes, because we left it on the free list and simply adjusted the
pointers to skip it. Hence if the corrected fs is then mounted again
on an older kernel while there is a AGFL wrap condition on disk, it
will pull the block from the AGFL and it's OK because it's still a
free block that isn't present in the ABTB/ABTC.

> I /think/ the answer to #2 is that a block only ends up on the AGFL
> after it's been removed from the freespace btrees, so the block pointed
> to in that last slot is still free and in fact can be used.

Correct.

> Therefore,
> the patch is correct and we don't need to write NULLAGBLOCK to the that
> last AGFL slot that we're never going to use again, and I'm worrying
> about nothing.

Well, there is still a worry here - mkfs will mark the entry as
NULLAGBLOCK, so if we take a filesystem like that with a wrapped
AGFL the older kernel will barf on a NULLAGBLOCK being allocated
from the AGFL. Nothing we can do about that, though, except to say
"run xfs_repair and all will be good again".

> xfs_repair writes 0xFF to the entire sector, rebuilds the freesp btrees,
> and moves the agfl to the start of the sector, so we're covered for that
> case.

Exactly.

> As for the question of whether or not an old kernel will notice flcount
> not fitting its world view w.r.t. fllast - flfirst + 1, I don't know if
> old kernels will notice; the current verifiers don't seem to check.

They don't.

> If we wanted to be really heavy handed I suppose we could set that last
> slot to sb_agblocks to stop all the agfl-119 kernels dead in their
> tracks, but I don't know that's necessary.

It still doesn't help us for the case that an existing mkfs is
usedi, an existing 4.8 kernel is used and then we wrap and then take
the fs back to an older kernel...

Still, after seeing the "make the fs on distro/kernel X; mount and
grow the fs to production size on different kernel Y; run production
on different kernel Z" container deployment infrastructure that
exposed the problem, I'd suggest that that are still some people we
cannot fix this problem for because their deployment system is
so convoluted that there's nothing we can do to avoid such problems
randomly occurring...

FWIW, this crazy deployment process is one of the reasons I didn't
make this a transactional correction. i.e. It won't make it to disk
unless there's a subsequent allocation/free done in the AG. THis
means mounting on a newer kernel will warn and correct in memory,
but won't modify on disk until it absolutely has to. Hence the
grwofs phase won't modify wrapped AGFLs on disk, and so shouldn't
cause problems for older kernels in this specific case.


Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 3/6] xfs: detect AGFL size mismatches
  2016-09-14 21:26     ` Dave Chinner
@ 2016-09-28 17:39       ` Eric Sandeen
  0 siblings, 0 replies; 15+ messages in thread
From: Eric Sandeen @ 2016-09-28 17:39 UTC (permalink / raw)
  To: xfs

On 9/14/16 4:26 PM, Dave Chinner wrote:

> Bad wrap case:
> 
> 	flfirst = 10	(invalid!)
> 	fllast = 0
> 	flcount = 2
> 	agfl_size = 10
> 	active = 0 - 10 + 1
> 	       = -9 + agfl_size
> 	       = 1 (correct as flfirst is invalid!)

Ok, I guess maybe I got confused w.r.t. the actual accounting vs.
the expected/correct accounting.

I saw "active = 1" and thought no, that's wrong, there are 2.
But active is what it /should/ be not what it /is/ ?

More comments would help I suppose, around either the first assignment
or the variable declaration, for this and for flcount.

(I guess flcount is actual number on the list, active is what
it /should/ be?)

> 	if (flfirst > agfl_size)		not true
> 	if (flfirst == agfl_size)		true, warn
> 
> 	if (fllast > agfl_size)			not true
> 	if (fllast == agfl_size)		not true
> 
> 	if (flcount > agfl_size + 1)		not true
> 	if (flcount == agfl_size)		not true
> 
> 	if (active <= 0)			not true
> 	if (flcount > active + 1)		not true
> 	if (flcount < active)			not true
> 	if (flcount != active)			true, warn
> 
> So, two correction needed warnings, which is correct because both
> flfirst and flcount need fixing due to being off by one.

ok
 
> The bad fllast case:
> 
> 	flfirst = 5
> 	fllast = 10	(invalid!)
> 	flcount = 6	
> 	agfl_size = 10
> 	active = fllast - flfirst + 1
> 	       = 10 - 5 + 1
> 	       = 6 (invalid as fllast is out of range)
> 
> 	if (flfirst > agfl_size)		not true
> 	if (flfirst == agfl_size)		not true
> 
> 	if (fllast > agfl_size)			not true
> 	if (fllast == agfl_size)		true, warn
> 
> 	if (flcount > agfl_size + 1)		not true
> 	if (flcount == agfl_size)		not true
> 
> 	if (active <= 0)			not true
> 	if (flcount > active + 1)		not true
> 	if (flcount < active)			not true
> 	if (flcount != active)			not true
> 
> So we've got a warning that a fllast correction will take place,
> which will also correct the flcount once the fllast index is
> updated.
> 
> But I think the case you are concerned about is a corner case of
> this one - a full AGFL, right?  Something that, in practice, will
> never happen as no single transaction will free or require a full
> AGFL worth of blocks in a single allocation/free operation.

Actually no, I hadn't caught that one ;)

> As such, I didn't actually consider it or test that case, so, it may
> be wrong.  Let's run the numbers:
> 
> 	flfirst = 0
> 	fllast = 10	(invalid!)
> 	flcount = 11	(invalid!)
> 	agfl_size = 10
> 	active = fllast - flfirst + 1
> 	       = 10 - 0 + 1
> 	       = 11 (invalid as fllast is out of range)
> 
> 	if (flfirst > agfl_size)		not true
> 	if (flfirst == agfl_size)		not true
> 
> 	if (fllast > agfl_size)			not true
> 	if (fllast == agfl_size)		true, warn
> 
> 	if (flcount > agfl_size + 1)		not true
> 	if (flcount == agfl_size)		not true
> 
> 	if (active <= 0)			not true
> 	if (flcount > active + 1)		not true
> 	if (flcount < active)			not true
> 	if (flcount != active)			not true
> 
> So it issues the same fllast warning as the not full, not wrapped
> case, and that correction also fixes up the flcount. Maybe it's the
> full wrapped case, which may be a pure flcount error?
> 
> 	flfirst = 5
> 	fllast = 4
> 	flcount = 11	(invalid!)
> 	agfl_size = 10
> 	active = fllast - flfirst + 1
> 	       = 4 - 5 + 1
> 	       = 10 (different to flcount!)
> 
> 	if (flfirst > agfl_size)		not true
> 	if (flfirst == agfl_size)		not true
> 
> 	if (fllast > agfl_size)			not true
> 	if (fllast == agfl_size)		not true
> 
> 	if (flcount > agfl_size + 1)		not true
> 	if (flcount == agfl_size)		not true
> 					(yes, that should warn)
> 
> 	if (active <= 0)			not true
> 	if (flcount > active + 1)		not true
> 	if (flcount < active)			not true
> 	if (flcount != active)			true, warn
> 
> So we do get a warning about the flcount being wrong because it's
> only one block different to the calculated active size (and hence it
> will be corrected), but no warning from the flcount being out of
> range by one block. So, yes, I think you are right, Eric, that the
> check should be against agfl_size + 1, even it it means we get
> multiple warnings...

Ah, ok :)  So this is essentially:

-	if (flcount == agfl_size)
+	if (flcount == agfl_size + 1)
		xfs_warn(mp, "AGF %u: freelist count needs correction(1)",
			be32_to_cpu(agf->agf_seqno));

for your patch, right.  Glad we agree.  :)

-Eric

> Thanks for thinking about this case!
> 
> Cheers,
> 
> Dave.
> 

_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/6] xfs: clean up XFS_AGFL_SIZE
  2016-09-02  2:27 ` [PATCH 1/6] xfs: clean up XFS_AGFL_SIZE Dave Chinner
@ 2018-01-03 22:27   ` Darrick J. Wong
  0 siblings, 0 replies; 15+ messages in thread
From: Darrick J. Wong @ 2018-01-03 22:27 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Fri, Sep 02, 2016 at 12:27:32PM +1000, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> The AGFL size calculation is about to get more complex, so lets turn
> the macro into a function first and remove the macro.
> 
> Signed-off-by: Dave Chinner <dchinner@redhat.com>
> ---
>  fs/xfs/libxfs/xfs_alloc.c  | 33 ++++++++++++++++++++++++++-------
>  fs/xfs/libxfs/xfs_alloc.h  |  2 ++
>  fs/xfs/libxfs/xfs_format.h | 13 +------------
>  fs/xfs/xfs_fsops.c         |  2 +-
>  4 files changed, 30 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
> index 05b5243..23559b9 100644
> --- a/fs/xfs/libxfs/xfs_alloc.c
> +++ b/fs/xfs/libxfs/xfs_alloc.c
> @@ -63,6 +63,24 @@ xfs_prealloc_blocks(
>  }
>  
>  /*
> + * Size of the AGFL.  For CRC-enabled filesystes we steal a couple of slots in
> + * the beginning of the block for a proper header with the location information
> + * and CRC.
> + */
> +int
> +xfs_agfl_size(
> +	struct xfs_mount	*mp)
> +
> +{
> +	int			size = mp->m_sb.sb_sectsize;

/me wonders if these ought to be unsigned ints, but otherwise:

Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> +
> +	if (!xfs_sb_version_hascrc(&mp->m_sb))
> +		return size / sizeof(xfs_agblock_t);
> +
> +	return (size - sizeof(struct xfs_agfl)) / sizeof(xfs_agblock_t);
> +}
> +
> +/*
>   * In order to avoid ENOSPC-related deadlock caused by out-of-order locking of
>   * AGF buffer (PV 947395), we place constraints on the relationship among
>   * actual allocations for data blocks, freelist blocks, and potential file data
> @@ -554,7 +572,7 @@ xfs_agfl_verify(
>  	if (bp->b_pag && be32_to_cpu(agfl->agfl_seqno) != bp->b_pag->pag_agno)
>  		return false;
>  
> -	for (i = 0; i < XFS_AGFL_SIZE(mp); i++) {
> +	for (i = 0; i < xfs_agfl_size(mp); i++) {
>  		if (be32_to_cpu(agfl->agfl_bno[i]) != NULLAGBLOCK &&
>  		    be32_to_cpu(agfl->agfl_bno[i]) >= mp->m_sb.sb_agblocks)
>  			return false;
> @@ -2230,7 +2248,7 @@ xfs_alloc_get_freelist(
>  	bno = be32_to_cpu(agfl_bno[be32_to_cpu(agf->agf_flfirst)]);
>  	be32_add_cpu(&agf->agf_flfirst, 1);
>  	xfs_trans_brelse(tp, agflbp);
> -	if (be32_to_cpu(agf->agf_flfirst) == XFS_AGFL_SIZE(mp))
> +	if (be32_to_cpu(agf->agf_flfirst) == xfs_agfl_size(mp))
>  		agf->agf_flfirst = 0;
>  
>  	pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
> @@ -2338,7 +2356,7 @@ xfs_alloc_put_freelist(
>  			be32_to_cpu(agf->agf_seqno), &agflbp)))
>  		return error;
>  	be32_add_cpu(&agf->agf_fllast, 1);
> -	if (be32_to_cpu(agf->agf_fllast) == XFS_AGFL_SIZE(mp))
> +	if (be32_to_cpu(agf->agf_fllast) == xfs_agfl_size(mp))
>  		agf->agf_fllast = 0;
>  
>  	pag = xfs_perag_get(mp, be32_to_cpu(agf->agf_seqno));
> @@ -2356,7 +2374,7 @@ xfs_alloc_put_freelist(
>  
>  	xfs_alloc_log_agf(tp, agbp, logflags);
>  
> -	ASSERT(be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp));
> +	ASSERT(be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp));
>  
>  	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agflbp);
>  	blockp = &agfl_bno[be32_to_cpu(agf->agf_fllast)];
> @@ -2377,6 +2395,7 @@ xfs_agf_verify(
>  	struct xfs_buf	*bp)
>   {
>  	struct xfs_agf	*agf = XFS_BUF_TO_AGF(bp);
> +	int32_t		agfl_size = xfs_agfl_size(mp);
>  
>  	if (xfs_sb_version_hascrc(&mp->m_sb)) {
>  		if (!uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid))
> @@ -2389,9 +2408,9 @@ 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) < XFS_AGFL_SIZE(mp) &&
> -	      be32_to_cpu(agf->agf_fllast) < XFS_AGFL_SIZE(mp) &&
> -	      be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)))
> +	      be32_to_cpu(agf->agf_flfirst) < agfl_size &&
> +	      be32_to_cpu(agf->agf_fllast) < agfl_size &&
> +	      be32_to_cpu(agf->agf_flcount) <= agfl_size))
>  		return false;
>  
>  	if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
> diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
> index 6fe2d6b..cfdd02f 100644
> --- a/fs/xfs/libxfs/xfs_alloc.h
> +++ b/fs/xfs/libxfs/xfs_alloc.h
> @@ -202,6 +202,8 @@ xfs_alloc_get_rec(
>  
>  int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
>  			xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
> +int xfs_agfl_size(struct xfs_mount *mp);
> +
>  int xfs_alloc_fix_freelist(struct xfs_alloc_arg *args, int flags);
>  int xfs_free_extent_fix_freelist(struct xfs_trans *tp, xfs_agnumber_t agno,
>  		struct xfs_buf **agbp);
> diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
> index 270fb5c..60085f3 100644
> --- a/fs/xfs/libxfs/xfs_format.h
> +++ b/fs/xfs/libxfs/xfs_format.h
> @@ -781,24 +781,13 @@ typedef struct xfs_agi {
>  		&(XFS_BUF_TO_AGFL(bp)->agfl_bno[0]) : \
>  		(__be32 *)(bp)->b_addr)
>  
> -/*
> - * Size of the AGFL.  For CRC-enabled filesystes we steal a couple of
> - * slots in the beginning of the block for a proper header with the
> - * location information and CRC.
> - */
> -#define XFS_AGFL_SIZE(mp) \
> -	(((mp)->m_sb.sb_sectsize - \
> -	 (xfs_sb_version_hascrc(&((mp)->m_sb)) ? \
> -		sizeof(struct xfs_agfl) : 0)) / \
> -	  sizeof(xfs_agblock_t))
> -
>  typedef struct xfs_agfl {
>  	__be32		agfl_magicnum;
>  	__be32		agfl_seqno;
>  	uuid_t		agfl_uuid;
>  	__be64		agfl_lsn;
>  	__be32		agfl_crc;
> -	__be32		agfl_bno[];	/* actually XFS_AGFL_SIZE(mp) */
> +	__be32		agfl_bno[];	/* actually xfs_agfl_size(mp) */
>  } __attribute__((packed)) xfs_agfl_t;
>  
>  #define XFS_AGFL_CRC_OFF	offsetof(struct xfs_agfl, agfl_crc)
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index 0b7f986..622ca71 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -285,7 +285,7 @@ xfs_growfs_data_private(
>  		}
>  
>  		agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
> -		for (bucket = 0; bucket < XFS_AGFL_SIZE(mp); bucket++)
> +		for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
>  			agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
>  
>  		error = xfs_bwrite(bp);
> -- 
> 2.8.0.rc3
> 
> _______________________________________________
> xfs mailing list
> xfs@oss.sgi.com
> http://oss.sgi.com/mailman/listinfo/xfs

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2018-01-03 22:29 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-02  2:27 [RFC PATCH 0/6] xfs: sort out the AGFL size mess Dave Chinner
2016-09-02  2:27 ` [PATCH 1/6] xfs: clean up XFS_AGFL_SIZE Dave Chinner
2018-01-03 22:27   ` Darrick J. Wong
2016-09-02  2:27 ` [PATCH 2/6] xfs: shadow agfl indexes in the per-ag structures Dave Chinner
2016-09-02 13:25   ` Eric Sandeen
2016-09-02 23:06     ` Dave Chinner
2016-09-02  2:27 ` [PATCH 3/6] xfs: detect AGFL size mismatches Dave Chinner
2016-09-13 23:39   ` Eric Sandeen
2016-09-14 21:26     ` Dave Chinner
2016-09-28 17:39       ` Eric Sandeen
2016-09-02  2:27 ` [PATCH 4/6] xfs: automatically fix up AGFL size issues Dave Chinner
2016-09-14 18:20   ` Darrick J. Wong
2016-09-14 21:50     ` Dave Chinner
2016-09-02  2:27 ` [PATCH 5/6] xfs: clean up AGFL index initialisation in growfs Dave Chinner
2016-09-02  2:27 ` [PATCH 6/6] xfs: use per-ag AGFL indexes everywhere Dave Chinner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.