diff options
-rw-r--r-- | fs/xfs/scrub/agheader_repair.c | 379 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.c | 27 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.h | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/scrub.c | 2 |
4 files changed, 400 insertions, 10 deletions
diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 1e96621ece3a..aa180492a4a5 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c | |||
@@ -17,12 +17,19 @@ | |||
17 | #include "xfs_sb.h" | 17 | #include "xfs_sb.h" |
18 | #include "xfs_inode.h" | 18 | #include "xfs_inode.h" |
19 | #include "xfs_alloc.h" | 19 | #include "xfs_alloc.h" |
20 | #include "xfs_alloc_btree.h" | ||
20 | #include "xfs_ialloc.h" | 21 | #include "xfs_ialloc.h" |
22 | #include "xfs_ialloc_btree.h" | ||
21 | #include "xfs_rmap.h" | 23 | #include "xfs_rmap.h" |
24 | #include "xfs_rmap_btree.h" | ||
25 | #include "xfs_refcount.h" | ||
26 | #include "xfs_refcount_btree.h" | ||
22 | #include "scrub/xfs_scrub.h" | 27 | #include "scrub/xfs_scrub.h" |
23 | #include "scrub/scrub.h" | 28 | #include "scrub/scrub.h" |
24 | #include "scrub/common.h" | 29 | #include "scrub/common.h" |
25 | #include "scrub/trace.h" | 30 | #include "scrub/trace.h" |
31 | #include "scrub/repair.h" | ||
32 | #include "scrub/bitmap.h" | ||
26 | 33 | ||
27 | /* Superblock */ | 34 | /* Superblock */ |
28 | 35 | ||
@@ -54,3 +61,375 @@ xrep_superblock( | |||
54 | xfs_trans_log_buf(sc->tp, bp, 0, BBTOB(bp->b_length) - 1); | 61 | xfs_trans_log_buf(sc->tp, bp, 0, BBTOB(bp->b_length) - 1); |
55 | return error; | 62 | return error; |
56 | } | 63 | } |
64 | |||
65 | /* AGF */ | ||
66 | |||
67 | struct xrep_agf_allocbt { | ||
68 | struct xfs_scrub *sc; | ||
69 | xfs_agblock_t freeblks; | ||
70 | xfs_agblock_t longest; | ||
71 | }; | ||
72 | |||
73 | /* Record free space shape information. */ | ||
74 | STATIC int | ||
75 | xrep_agf_walk_allocbt( | ||
76 | struct xfs_btree_cur *cur, | ||
77 | struct xfs_alloc_rec_incore *rec, | ||
78 | void *priv) | ||
79 | { | ||
80 | struct xrep_agf_allocbt *raa = priv; | ||
81 | int error = 0; | ||
82 | |||
83 | if (xchk_should_terminate(raa->sc, &error)) | ||
84 | return error; | ||
85 | |||
86 | raa->freeblks += rec->ar_blockcount; | ||
87 | if (rec->ar_blockcount > raa->longest) | ||
88 | raa->longest = rec->ar_blockcount; | ||
89 | return error; | ||
90 | } | ||
91 | |||
92 | /* Does this AGFL block look sane? */ | ||
93 | STATIC int | ||
94 | xrep_agf_check_agfl_block( | ||
95 | struct xfs_mount *mp, | ||
96 | xfs_agblock_t agbno, | ||
97 | void *priv) | ||
98 | { | ||
99 | struct xfs_scrub *sc = priv; | ||
100 | |||
101 | if (!xfs_verify_agbno(mp, sc->sa.agno, agbno)) | ||
102 | return -EFSCORRUPTED; | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * Offset within the xrep_find_ag_btree array for each btree type. Avoid the | ||
108 | * XFS_BTNUM_ names here to avoid creating a sparse array. | ||
109 | */ | ||
110 | enum { | ||
111 | XREP_AGF_BNOBT = 0, | ||
112 | XREP_AGF_CNTBT, | ||
113 | XREP_AGF_RMAPBT, | ||
114 | XREP_AGF_REFCOUNTBT, | ||
115 | XREP_AGF_END, | ||
116 | XREP_AGF_MAX | ||
117 | }; | ||
118 | |||
119 | /* Check a btree root candidate. */ | ||
120 | static inline bool | ||
121 | xrep_check_btree_root( | ||
122 | struct xfs_scrub *sc, | ||
123 | struct xrep_find_ag_btree *fab) | ||
124 | { | ||
125 | struct xfs_mount *mp = sc->mp; | ||
126 | xfs_agnumber_t agno = sc->sm->sm_agno; | ||
127 | |||
128 | return xfs_verify_agbno(mp, agno, fab->root) && | ||
129 | fab->height <= XFS_BTREE_MAXLEVELS; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * Given the btree roots described by *fab, find the roots, check them for | ||
134 | * sanity, and pass the root data back out via *fab. | ||
135 | * | ||
136 | * This is /also/ a chicken and egg problem because we have to use the rmapbt | ||
137 | * (rooted in the AGF) to find the btrees rooted in the AGF. We also have no | ||
138 | * idea if the btrees make any sense. If we hit obvious corruptions in those | ||
139 | * btrees we'll bail out. | ||
140 | */ | ||
141 | STATIC int | ||
142 | xrep_agf_find_btrees( | ||
143 | struct xfs_scrub *sc, | ||
144 | struct xfs_buf *agf_bp, | ||
145 | struct xrep_find_ag_btree *fab, | ||
146 | struct xfs_buf *agfl_bp) | ||
147 | { | ||
148 | struct xfs_agf *old_agf = XFS_BUF_TO_AGF(agf_bp); | ||
149 | int error; | ||
150 | |||
151 | /* Go find the root data. */ | ||
152 | error = xrep_find_ag_btree_roots(sc, agf_bp, fab, agfl_bp); | ||
153 | if (error) | ||
154 | return error; | ||
155 | |||
156 | /* We must find the bnobt, cntbt, and rmapbt roots. */ | ||
157 | if (!xrep_check_btree_root(sc, &fab[XREP_AGF_BNOBT]) || | ||
158 | !xrep_check_btree_root(sc, &fab[XREP_AGF_CNTBT]) || | ||
159 | !xrep_check_btree_root(sc, &fab[XREP_AGF_RMAPBT])) | ||
160 | return -EFSCORRUPTED; | ||
161 | |||
162 | /* | ||
163 | * We relied on the rmapbt to reconstruct the AGF. If we get a | ||
164 | * different root then something's seriously wrong. | ||
165 | */ | ||
166 | if (fab[XREP_AGF_RMAPBT].root != | ||
167 | be32_to_cpu(old_agf->agf_roots[XFS_BTNUM_RMAPi])) | ||
168 | return -EFSCORRUPTED; | ||
169 | |||
170 | /* We must find the refcountbt root if that feature is enabled. */ | ||
171 | if (xfs_sb_version_hasreflink(&sc->mp->m_sb) && | ||
172 | !xrep_check_btree_root(sc, &fab[XREP_AGF_REFCOUNTBT])) | ||
173 | return -EFSCORRUPTED; | ||
174 | |||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Reinitialize the AGF header, making an in-core copy of the old contents so | ||
180 | * that we know which in-core state needs to be reinitialized. | ||
181 | */ | ||
182 | STATIC void | ||
183 | xrep_agf_init_header( | ||
184 | struct xfs_scrub *sc, | ||
185 | struct xfs_buf *agf_bp, | ||
186 | struct xfs_agf *old_agf) | ||
187 | { | ||
188 | struct xfs_mount *mp = sc->mp; | ||
189 | struct xfs_agf *agf = XFS_BUF_TO_AGF(agf_bp); | ||
190 | |||
191 | memcpy(old_agf, agf, sizeof(*old_agf)); | ||
192 | memset(agf, 0, BBTOB(agf_bp->b_length)); | ||
193 | agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC); | ||
194 | agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION); | ||
195 | agf->agf_seqno = cpu_to_be32(sc->sa.agno); | ||
196 | agf->agf_length = cpu_to_be32(xfs_ag_block_count(mp, sc->sa.agno)); | ||
197 | agf->agf_flfirst = old_agf->agf_flfirst; | ||
198 | agf->agf_fllast = old_agf->agf_fllast; | ||
199 | agf->agf_flcount = old_agf->agf_flcount; | ||
200 | if (xfs_sb_version_hascrc(&mp->m_sb)) | ||
201 | uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid); | ||
202 | |||
203 | /* Mark the incore AGF data stale until we're done fixing things. */ | ||
204 | ASSERT(sc->sa.pag->pagf_init); | ||
205 | sc->sa.pag->pagf_init = 0; | ||
206 | } | ||
207 | |||
208 | /* Set btree root information in an AGF. */ | ||
209 | STATIC void | ||
210 | xrep_agf_set_roots( | ||
211 | struct xfs_scrub *sc, | ||
212 | struct xfs_agf *agf, | ||
213 | struct xrep_find_ag_btree *fab) | ||
214 | { | ||
215 | agf->agf_roots[XFS_BTNUM_BNOi] = | ||
216 | cpu_to_be32(fab[XREP_AGF_BNOBT].root); | ||
217 | agf->agf_levels[XFS_BTNUM_BNOi] = | ||
218 | cpu_to_be32(fab[XREP_AGF_BNOBT].height); | ||
219 | |||
220 | agf->agf_roots[XFS_BTNUM_CNTi] = | ||
221 | cpu_to_be32(fab[XREP_AGF_CNTBT].root); | ||
222 | agf->agf_levels[XFS_BTNUM_CNTi] = | ||
223 | cpu_to_be32(fab[XREP_AGF_CNTBT].height); | ||
224 | |||
225 | agf->agf_roots[XFS_BTNUM_RMAPi] = | ||
226 | cpu_to_be32(fab[XREP_AGF_RMAPBT].root); | ||
227 | agf->agf_levels[XFS_BTNUM_RMAPi] = | ||
228 | cpu_to_be32(fab[XREP_AGF_RMAPBT].height); | ||
229 | |||
230 | if (xfs_sb_version_hasreflink(&sc->mp->m_sb)) { | ||
231 | agf->agf_refcount_root = | ||
232 | cpu_to_be32(fab[XREP_AGF_REFCOUNTBT].root); | ||
233 | agf->agf_refcount_level = | ||
234 | cpu_to_be32(fab[XREP_AGF_REFCOUNTBT].height); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | /* Update all AGF fields which derive from btree contents. */ | ||
239 | STATIC int | ||
240 | xrep_agf_calc_from_btrees( | ||
241 | struct xfs_scrub *sc, | ||
242 | struct xfs_buf *agf_bp) | ||
243 | { | ||
244 | struct xrep_agf_allocbt raa = { .sc = sc }; | ||
245 | struct xfs_btree_cur *cur = NULL; | ||
246 | struct xfs_agf *agf = XFS_BUF_TO_AGF(agf_bp); | ||
247 | struct xfs_mount *mp = sc->mp; | ||
248 | xfs_agblock_t btreeblks; | ||
249 | xfs_agblock_t blocks; | ||
250 | int error; | ||
251 | |||
252 | /* Update the AGF counters from the bnobt. */ | ||
253 | cur = xfs_allocbt_init_cursor(mp, sc->tp, agf_bp, sc->sa.agno, | ||
254 | XFS_BTNUM_BNO); | ||
255 | error = xfs_alloc_query_all(cur, xrep_agf_walk_allocbt, &raa); | ||
256 | if (error) | ||
257 | goto err; | ||
258 | error = xfs_btree_count_blocks(cur, &blocks); | ||
259 | if (error) | ||
260 | goto err; | ||
261 | xfs_btree_del_cursor(cur, error); | ||
262 | btreeblks = blocks - 1; | ||
263 | agf->agf_freeblks = cpu_to_be32(raa.freeblks); | ||
264 | agf->agf_longest = cpu_to_be32(raa.longest); | ||
265 | |||
266 | /* Update the AGF counters from the cntbt. */ | ||
267 | cur = xfs_allocbt_init_cursor(mp, sc->tp, agf_bp, sc->sa.agno, | ||
268 | XFS_BTNUM_CNT); | ||
269 | error = xfs_btree_count_blocks(cur, &blocks); | ||
270 | if (error) | ||
271 | goto err; | ||
272 | xfs_btree_del_cursor(cur, error); | ||
273 | btreeblks += blocks - 1; | ||
274 | |||
275 | /* Update the AGF counters from the rmapbt. */ | ||
276 | cur = xfs_rmapbt_init_cursor(mp, sc->tp, agf_bp, sc->sa.agno); | ||
277 | error = xfs_btree_count_blocks(cur, &blocks); | ||
278 | if (error) | ||
279 | goto err; | ||
280 | xfs_btree_del_cursor(cur, error); | ||
281 | agf->agf_rmap_blocks = cpu_to_be32(blocks); | ||
282 | btreeblks += blocks - 1; | ||
283 | |||
284 | agf->agf_btreeblks = cpu_to_be32(btreeblks); | ||
285 | |||
286 | /* Update the AGF counters from the refcountbt. */ | ||
287 | if (xfs_sb_version_hasreflink(&mp->m_sb)) { | ||
288 | cur = xfs_refcountbt_init_cursor(mp, sc->tp, agf_bp, | ||
289 | sc->sa.agno); | ||
290 | error = xfs_btree_count_blocks(cur, &blocks); | ||
291 | if (error) | ||
292 | goto err; | ||
293 | xfs_btree_del_cursor(cur, error); | ||
294 | agf->agf_refcount_blocks = cpu_to_be32(blocks); | ||
295 | } | ||
296 | |||
297 | return 0; | ||
298 | err: | ||
299 | xfs_btree_del_cursor(cur, error); | ||
300 | return error; | ||
301 | } | ||
302 | |||
303 | /* Commit the new AGF and reinitialize the incore state. */ | ||
304 | STATIC int | ||
305 | xrep_agf_commit_new( | ||
306 | struct xfs_scrub *sc, | ||
307 | struct xfs_buf *agf_bp) | ||
308 | { | ||
309 | struct xfs_perag *pag; | ||
310 | struct xfs_agf *agf = XFS_BUF_TO_AGF(agf_bp); | ||
311 | |||
312 | /* Trigger fdblocks recalculation */ | ||
313 | xfs_force_summary_recalc(sc->mp); | ||
314 | |||
315 | /* Write this to disk. */ | ||
316 | xfs_trans_buf_set_type(sc->tp, agf_bp, XFS_BLFT_AGF_BUF); | ||
317 | xfs_trans_log_buf(sc->tp, agf_bp, 0, BBTOB(agf_bp->b_length) - 1); | ||
318 | |||
319 | /* Now reinitialize the in-core counters we changed. */ | ||
320 | pag = sc->sa.pag; | ||
321 | pag->pagf_btreeblks = be32_to_cpu(agf->agf_btreeblks); | ||
322 | pag->pagf_freeblks = be32_to_cpu(agf->agf_freeblks); | ||
323 | pag->pagf_longest = be32_to_cpu(agf->agf_longest); | ||
324 | pag->pagf_levels[XFS_BTNUM_BNOi] = | ||
325 | be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNOi]); | ||
326 | pag->pagf_levels[XFS_BTNUM_CNTi] = | ||
327 | be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi]); | ||
328 | pag->pagf_levels[XFS_BTNUM_RMAPi] = | ||
329 | be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAPi]); | ||
330 | pag->pagf_refcount_level = be32_to_cpu(agf->agf_refcount_level); | ||
331 | pag->pagf_init = 1; | ||
332 | |||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | /* Repair the AGF. v5 filesystems only. */ | ||
337 | int | ||
338 | xrep_agf( | ||
339 | struct xfs_scrub *sc) | ||
340 | { | ||
341 | struct xrep_find_ag_btree fab[XREP_AGF_MAX] = { | ||
342 | [XREP_AGF_BNOBT] = { | ||
343 | .rmap_owner = XFS_RMAP_OWN_AG, | ||
344 | .buf_ops = &xfs_allocbt_buf_ops, | ||
345 | .magic = XFS_ABTB_CRC_MAGIC, | ||
346 | }, | ||
347 | [XREP_AGF_CNTBT] = { | ||
348 | .rmap_owner = XFS_RMAP_OWN_AG, | ||
349 | .buf_ops = &xfs_allocbt_buf_ops, | ||
350 | .magic = XFS_ABTC_CRC_MAGIC, | ||
351 | }, | ||
352 | [XREP_AGF_RMAPBT] = { | ||
353 | .rmap_owner = XFS_RMAP_OWN_AG, | ||
354 | .buf_ops = &xfs_rmapbt_buf_ops, | ||
355 | .magic = XFS_RMAP_CRC_MAGIC, | ||
356 | }, | ||
357 | [XREP_AGF_REFCOUNTBT] = { | ||
358 | .rmap_owner = XFS_RMAP_OWN_REFC, | ||
359 | .buf_ops = &xfs_refcountbt_buf_ops, | ||
360 | .magic = XFS_REFC_CRC_MAGIC, | ||
361 | }, | ||
362 | [XREP_AGF_END] = { | ||
363 | .buf_ops = NULL, | ||
364 | }, | ||
365 | }; | ||
366 | struct xfs_agf old_agf; | ||
367 | struct xfs_mount *mp = sc->mp; | ||
368 | struct xfs_buf *agf_bp; | ||
369 | struct xfs_buf *agfl_bp; | ||
370 | struct xfs_agf *agf; | ||
371 | int error; | ||
372 | |||
373 | /* We require the rmapbt to rebuild anything. */ | ||
374 | if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) | ||
375 | return -EOPNOTSUPP; | ||
376 | |||
377 | xchk_perag_get(sc->mp, &sc->sa); | ||
378 | /* | ||
379 | * Make sure we have the AGF buffer, as scrub might have decided it | ||
380 | * was corrupt after xfs_alloc_read_agf failed with -EFSCORRUPTED. | ||
381 | */ | ||
382 | error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp, | ||
383 | XFS_AG_DADDR(mp, sc->sa.agno, XFS_AGF_DADDR(mp)), | ||
384 | XFS_FSS_TO_BB(mp, 1), 0, &agf_bp, NULL); | ||
385 | if (error) | ||
386 | return error; | ||
387 | agf_bp->b_ops = &xfs_agf_buf_ops; | ||
388 | agf = XFS_BUF_TO_AGF(agf_bp); | ||
389 | |||
390 | /* | ||
391 | * Load the AGFL so that we can screen out OWN_AG blocks that are on | ||
392 | * the AGFL now; these blocks might have once been part of the | ||
393 | * bno/cnt/rmap btrees but are not now. This is a chicken and egg | ||
394 | * problem: the AGF is corrupt, so we have to trust the AGFL contents | ||
395 | * because we can't do any serious cross-referencing with any of the | ||
396 | * btrees rooted in the AGF. If the AGFL contents are obviously bad | ||
397 | * then we'll bail out. | ||
398 | */ | ||
399 | error = xfs_alloc_read_agfl(mp, sc->tp, sc->sa.agno, &agfl_bp); | ||
400 | if (error) | ||
401 | return error; | ||
402 | |||
403 | /* | ||
404 | * Spot-check the AGFL blocks; if they're obviously corrupt then | ||
405 | * there's nothing we can do but bail out. | ||
406 | */ | ||
407 | error = xfs_agfl_walk(sc->mp, XFS_BUF_TO_AGF(agf_bp), agfl_bp, | ||
408 | xrep_agf_check_agfl_block, sc); | ||
409 | if (error) | ||
410 | return error; | ||
411 | |||
412 | /* | ||
413 | * Find the AGF btree roots. This is also a chicken-and-egg situation; | ||
414 | * see the function for more details. | ||
415 | */ | ||
416 | error = xrep_agf_find_btrees(sc, agf_bp, fab, agfl_bp); | ||
417 | if (error) | ||
418 | return error; | ||
419 | |||
420 | /* Start rewriting the header and implant the btrees we found. */ | ||
421 | xrep_agf_init_header(sc, agf_bp, &old_agf); | ||
422 | xrep_agf_set_roots(sc, agf, fab); | ||
423 | error = xrep_agf_calc_from_btrees(sc, agf_bp); | ||
424 | if (error) | ||
425 | goto out_revert; | ||
426 | |||
427 | /* Commit the changes and reinitialize incore state. */ | ||
428 | return xrep_agf_commit_new(sc, agf_bp); | ||
429 | |||
430 | out_revert: | ||
431 | /* Mark the incore AGF state stale and revert the AGF. */ | ||
432 | sc->sa.pag->pagf_init = 0; | ||
433 | memcpy(agf, &old_agf, sizeof(old_agf)); | ||
434 | return error; | ||
435 | } | ||
diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index 85b048b341a0..17cf48564390 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c | |||
@@ -128,9 +128,12 @@ xrep_roll_ag_trans( | |||
128 | int error; | 128 | int error; |
129 | 129 | ||
130 | /* Keep the AG header buffers locked so we can keep going. */ | 130 | /* Keep the AG header buffers locked so we can keep going. */ |
131 | xfs_trans_bhold(sc->tp, sc->sa.agi_bp); | 131 | if (sc->sa.agi_bp) |
132 | xfs_trans_bhold(sc->tp, sc->sa.agf_bp); | 132 | xfs_trans_bhold(sc->tp, sc->sa.agi_bp); |
133 | xfs_trans_bhold(sc->tp, sc->sa.agfl_bp); | 133 | if (sc->sa.agf_bp) |
134 | xfs_trans_bhold(sc->tp, sc->sa.agf_bp); | ||
135 | if (sc->sa.agfl_bp) | ||
136 | xfs_trans_bhold(sc->tp, sc->sa.agfl_bp); | ||
134 | 137 | ||
135 | /* Roll the transaction. */ | 138 | /* Roll the transaction. */ |
136 | error = xfs_trans_roll(&sc->tp); | 139 | error = xfs_trans_roll(&sc->tp); |
@@ -138,9 +141,12 @@ xrep_roll_ag_trans( | |||
138 | goto out_release; | 141 | goto out_release; |
139 | 142 | ||
140 | /* Join AG headers to the new transaction. */ | 143 | /* Join AG headers to the new transaction. */ |
141 | xfs_trans_bjoin(sc->tp, sc->sa.agi_bp); | 144 | if (sc->sa.agi_bp) |
142 | xfs_trans_bjoin(sc->tp, sc->sa.agf_bp); | 145 | xfs_trans_bjoin(sc->tp, sc->sa.agi_bp); |
143 | xfs_trans_bjoin(sc->tp, sc->sa.agfl_bp); | 146 | if (sc->sa.agf_bp) |
147 | xfs_trans_bjoin(sc->tp, sc->sa.agf_bp); | ||
148 | if (sc->sa.agfl_bp) | ||
149 | xfs_trans_bjoin(sc->tp, sc->sa.agfl_bp); | ||
144 | 150 | ||
145 | return 0; | 151 | return 0; |
146 | 152 | ||
@@ -150,9 +156,12 @@ out_release: | |||
150 | * buffers will be released during teardown on our way out | 156 | * buffers will be released during teardown on our way out |
151 | * of the kernel. | 157 | * of the kernel. |
152 | */ | 158 | */ |
153 | xfs_trans_bhold_release(sc->tp, sc->sa.agi_bp); | 159 | if (sc->sa.agi_bp) |
154 | xfs_trans_bhold_release(sc->tp, sc->sa.agf_bp); | 160 | xfs_trans_bhold_release(sc->tp, sc->sa.agi_bp); |
155 | xfs_trans_bhold_release(sc->tp, sc->sa.agfl_bp); | 161 | if (sc->sa.agf_bp) |
162 | xfs_trans_bhold_release(sc->tp, sc->sa.agf_bp); | ||
163 | if (sc->sa.agfl_bp) | ||
164 | xfs_trans_bhold_release(sc->tp, sc->sa.agfl_bp); | ||
156 | 165 | ||
157 | return error; | 166 | return error; |
158 | } | 167 | } |
diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h index 5a4e92221916..6f0903c51a47 100644 --- a/fs/xfs/scrub/repair.h +++ b/fs/xfs/scrub/repair.h | |||
@@ -58,6 +58,7 @@ int xrep_ino_dqattach(struct xfs_scrub *sc); | |||
58 | 58 | ||
59 | int xrep_probe(struct xfs_scrub *sc); | 59 | int xrep_probe(struct xfs_scrub *sc); |
60 | int xrep_superblock(struct xfs_scrub *sc); | 60 | int xrep_superblock(struct xfs_scrub *sc); |
61 | int xrep_agf(struct xfs_scrub *sc); | ||
61 | 62 | ||
62 | #else | 63 | #else |
63 | 64 | ||
@@ -81,6 +82,7 @@ xrep_calc_ag_resblks( | |||
81 | 82 | ||
82 | #define xrep_probe xrep_notsupported | 83 | #define xrep_probe xrep_notsupported |
83 | #define xrep_superblock xrep_notsupported | 84 | #define xrep_superblock xrep_notsupported |
85 | #define xrep_agf xrep_notsupported | ||
84 | 86 | ||
85 | #endif /* CONFIG_XFS_ONLINE_REPAIR */ | 87 | #endif /* CONFIG_XFS_ONLINE_REPAIR */ |
86 | 88 | ||
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 6efb926f3cf8..1e8a17c8e2b9 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c | |||
@@ -214,7 +214,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { | |||
214 | .type = ST_PERAG, | 214 | .type = ST_PERAG, |
215 | .setup = xchk_setup_fs, | 215 | .setup = xchk_setup_fs, |
216 | .scrub = xchk_agf, | 216 | .scrub = xchk_agf, |
217 | .repair = xrep_notsupported, | 217 | .repair = xrep_agf, |
218 | }, | 218 | }, |
219 | [XFS_SCRUB_TYPE_AGFL]= { /* agfl */ | 219 | [XFS_SCRUB_TYPE_AGFL]= { /* agfl */ |
220 | .type = ST_PERAG, | 220 | .type = ST_PERAG, |