aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2013-04-03 01:11:13 -0400
committerBen Myers <bpm@sgi.com>2013-04-21 15:54:46 -0400
commit4e0e6040c4052aff15a494ac05778f4086d24c33 (patch)
tree6116acb302eb4be87e0193cb7476812a4cbddaac /fs
parentee1a47ab0e77600fcbdf1c87d461bd8f3f63150d (diff)
xfs: add CRC checks to the AGF
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/xfs/xfs_ag.h23
-rw-r--r--fs/xfs/xfs_alloc.c80
-rw-r--r--fs/xfs/xfs_buf_item.h4
-rw-r--r--fs/xfs/xfs_fsops.c3
-rw-r--r--fs/xfs/xfs_log_recover.c8
5 files changed, 91 insertions, 27 deletions
diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h
index f2aeedb6a579..9f4815e0c96f 100644
--- a/fs/xfs/xfs_ag.h
+++ b/fs/xfs/xfs_ag.h
@@ -63,12 +63,29 @@ typedef struct xfs_agf {
63 __be32 agf_spare0; /* spare field */ 63 __be32 agf_spare0; /* spare field */
64 __be32 agf_levels[XFS_BTNUM_AGF]; /* btree levels */ 64 __be32 agf_levels[XFS_BTNUM_AGF]; /* btree levels */
65 __be32 agf_spare1; /* spare field */ 65 __be32 agf_spare1; /* spare field */
66
66 __be32 agf_flfirst; /* first freelist block's index */ 67 __be32 agf_flfirst; /* first freelist block's index */
67 __be32 agf_fllast; /* last freelist block's index */ 68 __be32 agf_fllast; /* last freelist block's index */
68 __be32 agf_flcount; /* count of blocks in freelist */ 69 __be32 agf_flcount; /* count of blocks in freelist */
69 __be32 agf_freeblks; /* total free blocks */ 70 __be32 agf_freeblks; /* total free blocks */
71
70 __be32 agf_longest; /* longest free space */ 72 __be32 agf_longest; /* longest free space */
71 __be32 agf_btreeblks; /* # of blocks held in AGF btrees */ 73 __be32 agf_btreeblks; /* # of blocks held in AGF btrees */
74 uuid_t agf_uuid; /* uuid of filesystem */
75
76 /*
77 * reserve some contiguous space for future logged fields before we add
78 * the unlogged fields. This makes the range logging via flags and
79 * structure offsets much simpler.
80 */
81 __be64 agf_spare64[16];
82
83 /* unlogged fields, written during buffer writeback. */
84 __be64 agf_lsn; /* last write sequence */
85 __be32 agf_crc; /* crc of agf sector */
86 __be32 agf_spare2;
87
88 /* structure must be padded to 64 bit alignment */
72} xfs_agf_t; 89} xfs_agf_t;
73 90
74#define XFS_AGF_MAGICNUM 0x00000001 91#define XFS_AGF_MAGICNUM 0x00000001
@@ -83,7 +100,8 @@ typedef struct xfs_agf {
83#define XFS_AGF_FREEBLKS 0x00000200 100#define XFS_AGF_FREEBLKS 0x00000200
84#define XFS_AGF_LONGEST 0x00000400 101#define XFS_AGF_LONGEST 0x00000400
85#define XFS_AGF_BTREEBLKS 0x00000800 102#define XFS_AGF_BTREEBLKS 0x00000800
86#define XFS_AGF_NUM_BITS 12 103#define XFS_AGF_UUID 0x00001000
104#define XFS_AGF_NUM_BITS 13
87#define XFS_AGF_ALL_BITS ((1 << XFS_AGF_NUM_BITS) - 1) 105#define XFS_AGF_ALL_BITS ((1 << XFS_AGF_NUM_BITS) - 1)
88 106
89#define XFS_AGF_FLAGS \ 107#define XFS_AGF_FLAGS \
@@ -98,7 +116,8 @@ typedef struct xfs_agf {
98 { XFS_AGF_FLCOUNT, "FLCOUNT" }, \ 116 { XFS_AGF_FLCOUNT, "FLCOUNT" }, \
99 { XFS_AGF_FREEBLKS, "FREEBLKS" }, \ 117 { XFS_AGF_FREEBLKS, "FREEBLKS" }, \
100 { XFS_AGF_LONGEST, "LONGEST" }, \ 118 { XFS_AGF_LONGEST, "LONGEST" }, \
101 { XFS_AGF_BTREEBLKS, "BTREEBLKS" } 119 { XFS_AGF_BTREEBLKS, "BTREEBLKS" }, \
120 { XFS_AGF_UUID, "UUID" }
102 121
103/* disk block (xfs_daddr_t) in the AG */ 122/* disk block (xfs_daddr_t) in the AG */
104#define XFS_AGF_DADDR(mp) ((xfs_daddr_t)(1 << (mp)->m_sectbb_log)) 123#define XFS_AGF_DADDR(mp) ((xfs_daddr_t)(1 << (mp)->m_sectbb_log))
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
index a8beb5c7dd60..c34f799aa92e 100644
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -33,7 +33,9 @@
33#include "xfs_alloc.h" 33#include "xfs_alloc.h"
34#include "xfs_extent_busy.h" 34#include "xfs_extent_busy.h"
35#include "xfs_error.h" 35#include "xfs_error.h"
36#include "xfs_cksum.h"
36#include "xfs_trace.h" 37#include "xfs_trace.h"
38#include "xfs_buf_item.h"
37 39
38struct workqueue_struct *xfs_alloc_wq; 40struct workqueue_struct *xfs_alloc_wq;
39 41
@@ -2058,11 +2060,14 @@ xfs_alloc_log_agf(
2058 offsetof(xfs_agf_t, agf_freeblks), 2060 offsetof(xfs_agf_t, agf_freeblks),
2059 offsetof(xfs_agf_t, agf_longest), 2061 offsetof(xfs_agf_t, agf_longest),
2060 offsetof(xfs_agf_t, agf_btreeblks), 2062 offsetof(xfs_agf_t, agf_btreeblks),
2063 offsetof(xfs_agf_t, agf_uuid),
2061 sizeof(xfs_agf_t) 2064 sizeof(xfs_agf_t)
2062 }; 2065 };
2063 2066
2064 trace_xfs_agf(tp->t_mountp, XFS_BUF_TO_AGF(bp), fields, _RET_IP_); 2067 trace_xfs_agf(tp->t_mountp, XFS_BUF_TO_AGF(bp), fields, _RET_IP_);
2065 2068
2069 xfs_trans_buf_set_type(tp, bp, XFS_BLF_AGF_BUF);
2070
2066 xfs_btree_offsets(fields, offsets, XFS_AGF_NUM_BITS, &first, &last); 2071 xfs_btree_offsets(fields, offsets, XFS_AGF_NUM_BITS, &first, &last);
2067 xfs_trans_log_buf(tp, bp, (uint)first, (uint)last); 2072 xfs_trans_log_buf(tp, bp, (uint)first, (uint)last);
2068} 2073}
@@ -2143,22 +2148,24 @@ xfs_alloc_put_freelist(
2143 return 0; 2148 return 0;
2144} 2149}
2145 2150
2146static void 2151static bool
2147xfs_agf_verify( 2152xfs_agf_verify(
2153 struct xfs_mount *mp,
2148 struct xfs_buf *bp) 2154 struct xfs_buf *bp)
2149 { 2155 {
2150 struct xfs_mount *mp = bp->b_target->bt_mount; 2156 struct xfs_agf *agf = XFS_BUF_TO_AGF(bp);
2151 struct xfs_agf *agf;
2152 int agf_ok;
2153 2157
2154 agf = XFS_BUF_TO_AGF(bp); 2158 if (xfs_sb_version_hascrc(&mp->m_sb) &&
2159 !uuid_equal(&agf->agf_uuid, &mp->m_sb.sb_uuid))
2160 return false;
2155 2161
2156 agf_ok = agf->agf_magicnum == cpu_to_be32(XFS_AGF_MAGIC) && 2162 if (!(agf->agf_magicnum == cpu_to_be32(XFS_AGF_MAGIC) &&
2157 XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum)) && 2163 XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum)) &&
2158 be32_to_cpu(agf->agf_freeblks) <= be32_to_cpu(agf->agf_length) && 2164 be32_to_cpu(agf->agf_freeblks) <= be32_to_cpu(agf->agf_length) &&
2159 be32_to_cpu(agf->agf_flfirst) < XFS_AGFL_SIZE(mp) && 2165 be32_to_cpu(agf->agf_flfirst) < XFS_AGFL_SIZE(mp) &&
2160 be32_to_cpu(agf->agf_fllast) < XFS_AGFL_SIZE(mp) && 2166 be32_to_cpu(agf->agf_fllast) < XFS_AGFL_SIZE(mp) &&
2161 be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp); 2167 be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp)))
2168 return false;
2162 2169
2163 /* 2170 /*
2164 * during growfs operations, the perag is not fully initialised, 2171 * during growfs operations, the perag is not fully initialised,
@@ -2166,33 +2173,58 @@ xfs_agf_verify(
2166 * use it by using uncached buffers that don't have the perag attached 2173 * use it by using uncached buffers that don't have the perag attached
2167 * so we can detect and avoid this problem. 2174 * so we can detect and avoid this problem.
2168 */ 2175 */
2169 if (bp->b_pag) 2176 if (bp->b_pag && be32_to_cpu(agf->agf_seqno) != bp->b_pag->pag_agno)
2170 agf_ok = agf_ok && be32_to_cpu(agf->agf_seqno) == 2177 return false;
2171 bp->b_pag->pag_agno;
2172 2178
2173 if (xfs_sb_version_haslazysbcount(&mp->m_sb)) 2179 if (xfs_sb_version_haslazysbcount(&mp->m_sb) &&
2174 agf_ok = agf_ok && be32_to_cpu(agf->agf_btreeblks) <= 2180 be32_to_cpu(agf->agf_btreeblks) > be32_to_cpu(agf->agf_length))
2175 be32_to_cpu(agf->agf_length); 2181 return false;
2182
2183 return true;;
2176 2184
2177 if (unlikely(XFS_TEST_ERROR(!agf_ok, mp, XFS_ERRTAG_ALLOC_READ_AGF,
2178 XFS_RANDOM_ALLOC_READ_AGF))) {
2179 XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, agf);
2180 xfs_buf_ioerror(bp, EFSCORRUPTED);
2181 }
2182} 2185}
2183 2186
2184static void 2187static void
2185xfs_agf_read_verify( 2188xfs_agf_read_verify(
2186 struct xfs_buf *bp) 2189 struct xfs_buf *bp)
2187{ 2190{
2188 xfs_agf_verify(bp); 2191 struct xfs_mount *mp = bp->b_target->bt_mount;
2192 int agf_ok = 1;
2193
2194 if (xfs_sb_version_hascrc(&mp->m_sb))
2195 agf_ok = xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
2196 offsetof(struct xfs_agf, agf_crc));
2197
2198 agf_ok = agf_ok && xfs_agf_verify(mp, bp);
2199
2200 if (unlikely(XFS_TEST_ERROR(!agf_ok, mp, XFS_ERRTAG_ALLOC_READ_AGF,
2201 XFS_RANDOM_ALLOC_READ_AGF))) {
2202 XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
2203 xfs_buf_ioerror(bp, EFSCORRUPTED);
2204 }
2189} 2205}
2190 2206
2191static void 2207static void
2192xfs_agf_write_verify( 2208xfs_agf_write_verify(
2193 struct xfs_buf *bp) 2209 struct xfs_buf *bp)
2194{ 2210{
2195 xfs_agf_verify(bp); 2211 struct xfs_mount *mp = bp->b_target->bt_mount;
2212 struct xfs_buf_log_item *bip = bp->b_fspriv;
2213
2214 if (!xfs_agf_verify(mp, bp)) {
2215 XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
2216 xfs_buf_ioerror(bp, EFSCORRUPTED);
2217 return;
2218 }
2219
2220 if (!xfs_sb_version_hascrc(&mp->m_sb))
2221 return;
2222
2223 if (bip)
2224 XFS_BUF_TO_AGF(bp)->agf_lsn = cpu_to_be64(bip->bli_item.li_lsn);
2225
2226 xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length),
2227 offsetof(struct xfs_agf, agf_crc));
2196} 2228}
2197 2229
2198const struct xfs_buf_ops xfs_agf_buf_ops = { 2230const struct xfs_buf_ops xfs_agf_buf_ops = {
diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h
index 101ef8377f1b..76bd7a167a70 100644
--- a/fs/xfs/xfs_buf_item.h
+++ b/fs/xfs/xfs_buf_item.h
@@ -45,12 +45,14 @@ extern kmem_zone_t *xfs_buf_item_zone;
45 * once the changes have been replayed into the buffer. 45 * once the changes have been replayed into the buffer.
46 */ 46 */
47#define XFS_BLF_BTREE_BUF (1<<5) 47#define XFS_BLF_BTREE_BUF (1<<5)
48#define XFS_BLF_AGF_BUF (1<<6)
48 49
49#define XFS_BLF_TYPE_MASK \ 50#define XFS_BLF_TYPE_MASK \
50 (XFS_BLF_UDQUOT_BUF | \ 51 (XFS_BLF_UDQUOT_BUF | \
51 XFS_BLF_PDQUOT_BUF | \ 52 XFS_BLF_PDQUOT_BUF | \
52 XFS_BLF_GDQUOT_BUF | \ 53 XFS_BLF_GDQUOT_BUF | \
53 XFS_BLF_BTREE_BUF) 54 XFS_BLF_BTREE_BUF | \
55 XFS_BLF_AGF_BUF)
54 56
55#define XFS_BLF_CHUNK 128 57#define XFS_BLF_CHUNK 128
56#define XFS_BLF_SHIFT 7 58#define XFS_BLF_SHIFT 7
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 6fe286a8e29b..b6cca27e9132 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -247,6 +247,9 @@ xfs_growfs_data_private(
247 tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp); 247 tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp);
248 agf->agf_freeblks = cpu_to_be32(tmpsize); 248 agf->agf_freeblks = cpu_to_be32(tmpsize);
249 agf->agf_longest = cpu_to_be32(tmpsize); 249 agf->agf_longest = cpu_to_be32(tmpsize);
250 if (xfs_sb_version_hascrc(&mp->m_sb))
251 uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_uuid);
252
250 error = xfs_bwrite(bp); 253 error = xfs_bwrite(bp);
251 xfs_buf_relse(bp); 254 xfs_buf_relse(bp);
252 if (error) 255 if (error)
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 3762ce2e99fc..dadf24566d16 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1953,6 +1953,14 @@ xlog_recover_do_reg_buffer(
1953 break; 1953 break;
1954 } 1954 }
1955 break; 1955 break;
1956 case XFS_BLF_AGF_BUF:
1957 if (*(__be32 *)bp->b_addr != cpu_to_be32(XFS_AGF_MAGIC)) {
1958 xfs_warn(mp, "Bad AGF block magic!");
1959 ASSERT(0);
1960 break;
1961 }
1962 bp->b_ops = &xfs_agf_buf_ops;
1963 break;
1956 default: 1964 default:
1957 break; 1965 break;
1958 } 1966 }