xfs: add CRC checks to the AGF
authorDave Chinner <dchinner@redhat.com>
Wed, 3 Apr 2013 05:11:13 +0000 (16:11 +1100)
committerBen Myers <bpm@sgi.com>
Sun, 21 Apr 2013 19:54:46 +0000 (14:54 -0500)
The AGF already has some self identifying fields (e.g. the sequence
number) so we only need to add the uuid to it to identify the
filesystem it belongs to. The location is fixed based on the
sequence number, so there's no need to add a block number, either.

Hence the only additional fields are the CRC and LSN fields. These
are unlogged, so place some space between the end of the logged
fields and them so that future expansion of the AGF for logged
fields can be placed adjacent to the existing logged fields and
hence not complicate the field-derived range based logging we
currently have.

Based originally on a patch from myself, modified further by
Christoph Hellwig and then modified again to fit into the
verifier structure with additional fields by myself. The multiple
signed-off-by tags indicate the age and history of this patch.

Signed-off-by: Dave Chinner <dgc@sgi.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Ben Myers <bpm@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
fs/xfs/xfs_ag.h
fs/xfs/xfs_alloc.c
fs/xfs/xfs_buf_item.h
fs/xfs/xfs_fsops.c
fs/xfs/xfs_log_recover.c

index f2aeedb6a579f0e45246feade8ca68dd4a8652ff..9f4815e0c96ffc45a497a4ad0c359e3a91706f65 100644 (file)
@@ -63,12 +63,29 @@ typedef struct xfs_agf {
        __be32          agf_spare0;     /* spare field */
        __be32          agf_levels[XFS_BTNUM_AGF];      /* btree levels */
        __be32          agf_spare1;     /* spare field */
+
        __be32          agf_flfirst;    /* first freelist block's index */
        __be32          agf_fllast;     /* last freelist block's index */
        __be32          agf_flcount;    /* count of blocks in freelist */
        __be32          agf_freeblks;   /* total free blocks */
+
        __be32          agf_longest;    /* longest free space */
        __be32          agf_btreeblks;  /* # of blocks held in AGF btrees */
+       uuid_t          agf_uuid;       /* uuid of filesystem */
+
+       /*
+        * reserve some contiguous space for future logged fields before we add
+        * the unlogged fields. This makes the range logging via flags and
+        * structure offsets much simpler.
+        */
+       __be64          agf_spare64[16];
+
+       /* unlogged fields, written during buffer writeback. */
+       __be64          agf_lsn;        /* last write sequence */
+       __be32          agf_crc;        /* crc of agf sector */
+       __be32          agf_spare2;
+
+       /* structure must be padded to 64 bit alignment */
 } xfs_agf_t;
 
 #define        XFS_AGF_MAGICNUM        0x00000001
@@ -83,7 +100,8 @@ typedef struct xfs_agf {
 #define        XFS_AGF_FREEBLKS        0x00000200
 #define        XFS_AGF_LONGEST         0x00000400
 #define        XFS_AGF_BTREEBLKS       0x00000800
-#define        XFS_AGF_NUM_BITS        12
+#define        XFS_AGF_UUID            0x00001000
+#define        XFS_AGF_NUM_BITS        13
 #define        XFS_AGF_ALL_BITS        ((1 << XFS_AGF_NUM_BITS) - 1)
 
 #define XFS_AGF_FLAGS \
@@ -98,7 +116,8 @@ typedef struct xfs_agf {
        { XFS_AGF_FLCOUNT,      "FLCOUNT" }, \
        { XFS_AGF_FREEBLKS,     "FREEBLKS" }, \
        { XFS_AGF_LONGEST,      "LONGEST" }, \
-       { XFS_AGF_BTREEBLKS,    "BTREEBLKS" }
+       { XFS_AGF_BTREEBLKS,    "BTREEBLKS" }, \
+       { XFS_AGF_UUID,         "UUID" }
 
 /* disk block (xfs_daddr_t) in the AG */
 #define XFS_AGF_DADDR(mp)      ((xfs_daddr_t)(1 << (mp)->m_sectbb_log))
index a8beb5c7dd60e2d7b8b114fd0827ec27f37230ab..c34f799aa92e76f987733807e8935cdeea64bea2 100644 (file)
@@ -33,7 +33,9 @@
 #include "xfs_alloc.h"
 #include "xfs_extent_busy.h"
 #include "xfs_error.h"
+#include "xfs_cksum.h"
 #include "xfs_trace.h"
+#include "xfs_buf_item.h"
 
 struct workqueue_struct *xfs_alloc_wq;
 
@@ -2058,11 +2060,14 @@ xfs_alloc_log_agf(
                offsetof(xfs_agf_t, agf_freeblks),
                offsetof(xfs_agf_t, agf_longest),
                offsetof(xfs_agf_t, agf_btreeblks),
+               offsetof(xfs_agf_t, agf_uuid),
                sizeof(xfs_agf_t)
        };
 
        trace_xfs_agf(tp->t_mountp, XFS_BUF_TO_AGF(bp), fields, _RET_IP_);
 
+       xfs_trans_buf_set_type(tp, bp, XFS_BLF_AGF_BUF);
+
        xfs_btree_offsets(fields, offsets, XFS_AGF_NUM_BITS, &first, &last);
        xfs_trans_log_buf(tp, bp, (uint)first, (uint)last);
 }
@@ -2143,22 +2148,24 @@ xfs_alloc_put_freelist(
        return 0;
 }
 
-static void
+static bool
 xfs_agf_verify(
+       struct xfs_mount *mp,
        struct xfs_buf  *bp)
  {
-       struct xfs_mount *mp = bp->b_target->bt_mount;
-       struct xfs_agf  *agf;
-       int             agf_ok;
+       struct xfs_agf  *agf = XFS_BUF_TO_AGF(bp);
 
-       agf = XFS_BUF_TO_AGF(bp);
+       if (xfs_sb_version_hascrc(&mp->m_sb) &&
+           !uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_uuid))
+                       return false;
 
-       agf_ok = 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);
+       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)))
+               return false;
 
        /*
         * during growfs operations, the perag is not fully initialised,
@@ -2166,33 +2173,58 @@ xfs_agf_verify(
         * use it by using uncached buffers that don't have the perag attached
         * so we can detect and avoid this problem.
         */
-       if (bp->b_pag)
-               agf_ok = agf_ok && be32_to_cpu(agf->agf_seqno) ==
-                                               bp->b_pag->pag_agno;
+       if (bp->b_pag && be32_to_cpu(agf->agf_seqno) != bp->b_pag->pag_agno)
+               return false;
 
-       if (xfs_sb_version_haslazysbcount(&mp->m_sb))
-               agf_ok = agf_ok && be32_to_cpu(agf->agf_btreeblks) <=
-                                               be32_to_cpu(agf->agf_length);
+       if (xfs_sb_version_haslazysbcount(&mp->m_sb) &&
+           be32_to_cpu(agf->agf_btreeblks) > be32_to_cpu(agf->agf_length))
+               return false;
+
+       return true;;
 
-       if (unlikely(XFS_TEST_ERROR(!agf_ok, mp, XFS_ERRTAG_ALLOC_READ_AGF,
-                       XFS_RANDOM_ALLOC_READ_AGF))) {
-               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, agf);
-               xfs_buf_ioerror(bp, EFSCORRUPTED);
-       }
 }
 
 static void
 xfs_agf_read_verify(
        struct xfs_buf  *bp)
 {
-       xfs_agf_verify(bp);
+       struct xfs_mount *mp = bp->b_target->bt_mount;
+       int             agf_ok = 1;
+
+       if (xfs_sb_version_hascrc(&mp->m_sb))
+               agf_ok = xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
+                                         offsetof(struct xfs_agf, agf_crc));
+
+       agf_ok = agf_ok && xfs_agf_verify(mp, bp);
+
+       if (unlikely(XFS_TEST_ERROR(!agf_ok, mp, XFS_ERRTAG_ALLOC_READ_AGF,
+                       XFS_RANDOM_ALLOC_READ_AGF))) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       }
 }
 
 static void
 xfs_agf_write_verify(
        struct xfs_buf  *bp)
 {
-       xfs_agf_verify(bp);
+       struct xfs_mount *mp = bp->b_target->bt_mount;
+       struct xfs_buf_log_item *bip = bp->b_fspriv;
+
+       if (!xfs_agf_verify(mp, bp)) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+               return;
+       }
+
+       if (!xfs_sb_version_hascrc(&mp->m_sb))
+               return;
+
+       if (bip)
+               XFS_BUF_TO_AGF(bp)->agf_lsn = cpu_to_be64(bip->bli_item.li_lsn);
+
+       xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
+                        offsetof(struct xfs_agf, agf_crc));
 }
 
 const struct xfs_buf_ops xfs_agf_buf_ops = {
index 101ef8377f1bc96695b7844d69209d40ad41eb18..76bd7a167a70d7368d2e3d8a3356027c291893a5 100644 (file)
@@ -45,12 +45,14 @@ extern kmem_zone_t  *xfs_buf_item_zone;
  * once the changes have been replayed into the buffer.
  */
 #define XFS_BLF_BTREE_BUF      (1<<5)
+#define XFS_BLF_AGF_BUF                (1<<6)
 
 #define XFS_BLF_TYPE_MASK      \
                (XFS_BLF_UDQUOT_BUF | \
                 XFS_BLF_PDQUOT_BUF | \
                 XFS_BLF_GDQUOT_BUF | \
-                XFS_BLF_BTREE_BUF)
+                XFS_BLF_BTREE_BUF | \
+                XFS_BLF_AGF_BUF)
 
 #define        XFS_BLF_CHUNK           128
 #define        XFS_BLF_SHIFT           7
index 6fe286a8e29b35ee2a95879234768532147b0618..b6cca27e91329383fe20c260b735ac8110cc5912 100644 (file)
@@ -247,6 +247,9 @@ xfs_growfs_data_private(
                tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp);
                agf->agf_freeblks = cpu_to_be32(tmpsize);
                agf->agf_longest = cpu_to_be32(tmpsize);
+               if (xfs_sb_version_hascrc(&mp->m_sb))
+                       uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_uuid);
+
                error = xfs_bwrite(bp);
                xfs_buf_relse(bp);
                if (error)
index 3762ce2e99fc42e4a44e9895f90956992c0aaa72..dadf24566d16332a9fd339a8186314f743558c99 100644 (file)
@@ -1953,6 +1953,14 @@ xlog_recover_do_reg_buffer(
                        break;
                }
                break;
+       case XFS_BLF_AGF_BUF:
+               if (*(__be32 *)bp->b_addr != cpu_to_be32(XFS_AGF_MAGIC)) {
+                       xfs_warn(mp, "Bad AGF block magic!");
+                       ASSERT(0);
+                       break;
+               }
+               bp->b_ops = &xfs_agf_buf_ops;
+               break;
        default:
                break;
        }