aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2018-05-30 01:18:08 -0400
committerDarrick J. Wong <darrick.wong@oracle.com>2018-05-30 11:03:14 -0400
commit0a9633fa2f9a7ae52e2068fd706e2dee10be94a0 (patch)
tree4d0b9ccafc39c5a211520968bd5a5858f7b0e896
parent51863d7dd77dd27a35b12b37c7caf8679903b6ae (diff)
xfs: add helpers to deal with transaction allocation and rolling
For repairs, we need to reserve at least as many blocks as we think we're going to need to rebuild the data structure, and we're going to need some helpers to roll transactions while maintaining locks on the AG headers so that other threads cannot wander into the middle of a repair. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
-rw-r--r--fs/xfs/scrub/bmap.c2
-rw-r--r--fs/xfs/scrub/common.c21
-rw-r--r--fs/xfs/scrub/common.h2
-rw-r--r--fs/xfs/scrub/inode.c4
-rw-r--r--fs/xfs/scrub/repair.c160
-rw-r--r--fs/xfs/scrub/repair.h12
6 files changed, 194 insertions, 7 deletions
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 42a115e83739..eeadb33a701c 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -74,7 +74,7 @@ xfs_scrub_setup_inode_bmap(
74 } 74 }
75 75
76 /* Got the inode, lock it and we're ready to go. */ 76 /* Got the inode, lock it and we're ready to go. */
77 error = xfs_scrub_trans_alloc(sc); 77 error = xfs_scrub_trans_alloc(sc, 0);
78 if (error) 78 if (error)
79 goto out; 79 goto out;
80 sc->ilock_flags |= XFS_ILOCK_EXCL; 80 sc->ilock_flags |= XFS_ILOCK_EXCL;
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index d3e5adc96411..41198a5f872c 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -51,6 +51,7 @@
51#include "scrub/common.h" 51#include "scrub/common.h"
52#include "scrub/trace.h" 52#include "scrub/trace.h"
53#include "scrub/btree.h" 53#include "scrub/btree.h"
54#include "scrub/repair.h"
54 55
55/* Common code for the metadata scrubbers. */ 56/* Common code for the metadata scrubbers. */
56 57
@@ -590,11 +591,22 @@ xfs_scrub_perag_get(
590/* 591/*
591 * Grab an empty transaction so that we can re-grab locked buffers if 592 * Grab an empty transaction so that we can re-grab locked buffers if
592 * one of our btrees turns out to be cyclic. 593 * one of our btrees turns out to be cyclic.
594 *
595 * If we're going to repair something, we need to ask for the largest possible
596 * log reservation so that we can handle the worst case scenario for metadata
597 * updates while rebuilding a metadata item. We also need to reserve as many
598 * blocks in the head transaction as we think we're going to need to rebuild
599 * the metadata object.
593 */ 600 */
594int 601int
595xfs_scrub_trans_alloc( 602xfs_scrub_trans_alloc(
596 struct xfs_scrub_context *sc) 603 struct xfs_scrub_context *sc,
604 uint resblks)
597{ 605{
606 if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)
607 return xfs_trans_alloc(sc->mp, &M_RES(sc->mp)->tr_itruncate,
608 resblks, 0, 0, &sc->tp);
609
598 return xfs_trans_alloc_empty(sc->mp, &sc->tp); 610 return xfs_trans_alloc_empty(sc->mp, &sc->tp);
599} 611}
600 612
@@ -604,7 +616,10 @@ xfs_scrub_setup_fs(
604 struct xfs_scrub_context *sc, 616 struct xfs_scrub_context *sc,
605 struct xfs_inode *ip) 617 struct xfs_inode *ip)
606{ 618{
607 return xfs_scrub_trans_alloc(sc); 619 uint resblks;
620
621 resblks = xfs_repair_calc_ag_resblks(sc);
622 return xfs_scrub_trans_alloc(sc, resblks);
608} 623}
609 624
610/* Set us up with AG headers and btree cursors. */ 625/* Set us up with AG headers and btree cursors. */
@@ -734,7 +749,7 @@ xfs_scrub_setup_inode_contents(
734 /* Got the inode, lock it and we're ready to go. */ 749 /* Got the inode, lock it and we're ready to go. */
735 sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL; 750 sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
736 xfs_ilock(sc->ip, sc->ilock_flags); 751 xfs_ilock(sc->ip, sc->ilock_flags);
737 error = xfs_scrub_trans_alloc(sc); 752 error = xfs_scrub_trans_alloc(sc, resblks);
738 if (error) 753 if (error)
739 goto out; 754 goto out;
740 sc->ilock_flags |= XFS_ILOCK_EXCL; 755 sc->ilock_flags |= XFS_ILOCK_EXCL;
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
index fbb91a7144fd..76bb2d1d808c 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -38,7 +38,7 @@ xfs_scrub_should_terminate(
38 return false; 38 return false;
39} 39}
40 40
41int xfs_scrub_trans_alloc(struct xfs_scrub_context *sc); 41int xfs_scrub_trans_alloc(struct xfs_scrub_context *sc, uint resblks);
42bool xfs_scrub_process_error(struct xfs_scrub_context *sc, xfs_agnumber_t agno, 42bool xfs_scrub_process_error(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
43 xfs_agblock_t bno, int *error); 43 xfs_agblock_t bno, int *error);
44bool xfs_scrub_fblock_process_error(struct xfs_scrub_context *sc, int whichfork, 44bool xfs_scrub_fblock_process_error(struct xfs_scrub_context *sc, int whichfork,
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 550c0cf70a92..0c696f7018de 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -67,7 +67,7 @@ xfs_scrub_setup_inode(
67 break; 67 break;
68 case -EFSCORRUPTED: 68 case -EFSCORRUPTED:
69 case -EFSBADCRC: 69 case -EFSBADCRC:
70 return xfs_scrub_trans_alloc(sc); 70 return xfs_scrub_trans_alloc(sc, 0);
71 default: 71 default:
72 return error; 72 return error;
73 } 73 }
@@ -75,7 +75,7 @@ xfs_scrub_setup_inode(
75 /* Got the inode, lock it and we're ready to go. */ 75 /* Got the inode, lock it and we're ready to go. */
76 sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL; 76 sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
77 xfs_ilock(sc->ip, sc->ilock_flags); 77 xfs_ilock(sc->ip, sc->ilock_flags);
78 error = xfs_scrub_trans_alloc(sc); 78 error = xfs_scrub_trans_alloc(sc, 0);
79 if (error) 79 if (error)
80 goto out; 80 goto out;
81 sc->ilock_flags |= XFS_ILOCK_EXCL; 81 sc->ilock_flags |= XFS_ILOCK_EXCL;
diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c
index be30825c47c6..d86f8731a78f 100644
--- a/fs/xfs/scrub/repair.c
+++ b/fs/xfs/scrub/repair.c
@@ -128,3 +128,163 @@ xfs_repair_probe(
128 128
129 return 0; 129 return 0;
130} 130}
131
132/*
133 * Roll a transaction, keeping the AG headers locked and reinitializing
134 * the btree cursors.
135 */
136int
137xfs_repair_roll_ag_trans(
138 struct xfs_scrub_context *sc)
139{
140 int error;
141
142 /* Keep the AG header buffers locked so we can keep going. */
143 xfs_trans_bhold(sc->tp, sc->sa.agi_bp);
144 xfs_trans_bhold(sc->tp, sc->sa.agf_bp);
145 xfs_trans_bhold(sc->tp, sc->sa.agfl_bp);
146
147 /* Roll the transaction. */
148 error = xfs_trans_roll(&sc->tp);
149 if (error)
150 goto out_release;
151
152 /* Join AG headers to the new transaction. */
153 xfs_trans_bjoin(sc->tp, sc->sa.agi_bp);
154 xfs_trans_bjoin(sc->tp, sc->sa.agf_bp);
155 xfs_trans_bjoin(sc->tp, sc->sa.agfl_bp);
156
157 return 0;
158
159out_release:
160 /*
161 * Rolling failed, so release the hold on the buffers. The
162 * buffers will be released during teardown on our way out
163 * of the kernel.
164 */
165 xfs_trans_bhold_release(sc->tp, sc->sa.agi_bp);
166 xfs_trans_bhold_release(sc->tp, sc->sa.agf_bp);
167 xfs_trans_bhold_release(sc->tp, sc->sa.agfl_bp);
168
169 return error;
170}
171
172/*
173 * Does the given AG have enough space to rebuild a btree? Neither AG
174 * reservation can be critical, and we must have enough space (factoring
175 * in AG reservations) to construct a whole btree.
176 */
177bool
178xfs_repair_ag_has_space(
179 struct xfs_perag *pag,
180 xfs_extlen_t nr_blocks,
181 enum xfs_ag_resv_type type)
182{
183 return !xfs_ag_resv_critical(pag, XFS_AG_RESV_RMAPBT) &&
184 !xfs_ag_resv_critical(pag, XFS_AG_RESV_METADATA) &&
185 pag->pagf_freeblks > xfs_ag_resv_needed(pag, type) + nr_blocks;
186}
187
188/*
189 * Figure out how many blocks to reserve for an AG repair. We calculate the
190 * worst case estimate for the number of blocks we'd need to rebuild one of
191 * any type of per-AG btree.
192 */
193xfs_extlen_t
194xfs_repair_calc_ag_resblks(
195 struct xfs_scrub_context *sc)
196{
197 struct xfs_mount *mp = sc->mp;
198 struct xfs_scrub_metadata *sm = sc->sm;
199 struct xfs_perag *pag;
200 struct xfs_buf *bp;
201 xfs_agino_t icount = 0;
202 xfs_extlen_t aglen = 0;
203 xfs_extlen_t usedlen;
204 xfs_extlen_t freelen;
205 xfs_extlen_t bnobt_sz;
206 xfs_extlen_t inobt_sz;
207 xfs_extlen_t rmapbt_sz;
208 xfs_extlen_t refcbt_sz;
209 int error;
210
211 if (!(sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR))
212 return 0;
213
214 /* Use in-core counters if possible. */
215 pag = xfs_perag_get(mp, sm->sm_agno);
216 if (pag->pagi_init)
217 icount = pag->pagi_count;
218
219 /*
220 * Otherwise try to get the actual counters from disk; if not, make
221 * some worst case assumptions.
222 */
223 if (icount == 0) {
224 error = xfs_ialloc_read_agi(mp, NULL, sm->sm_agno, &bp);
225 if (error) {
226 icount = mp->m_sb.sb_agblocks / mp->m_sb.sb_inopblock;
227 } else {
228 icount = pag->pagi_count;
229 xfs_buf_relse(bp);
230 }
231 }
232
233 /* Now grab the block counters from the AGF. */
234 error = xfs_alloc_read_agf(mp, NULL, sm->sm_agno, 0, &bp);
235 if (error) {
236 aglen = mp->m_sb.sb_agblocks;
237 freelen = aglen;
238 usedlen = aglen;
239 } else {
240 aglen = be32_to_cpu(XFS_BUF_TO_AGF(bp)->agf_length);
241 freelen = pag->pagf_freeblks;
242 usedlen = aglen - freelen;
243 xfs_buf_relse(bp);
244 }
245 xfs_perag_put(pag);
246
247 trace_xfs_repair_calc_ag_resblks(mp, sm->sm_agno, icount, aglen,
248 freelen, usedlen);
249
250 /*
251 * Figure out how many blocks we'd need worst case to rebuild
252 * each type of btree. Note that we can only rebuild the
253 * bnobt/cntbt or inobt/finobt as pairs.
254 */
255 bnobt_sz = 2 * xfs_allocbt_calc_size(mp, freelen);
256 if (xfs_sb_version_hassparseinodes(&mp->m_sb))
257 inobt_sz = xfs_iallocbt_calc_size(mp, icount /
258 XFS_INODES_PER_HOLEMASK_BIT);
259 else
260 inobt_sz = xfs_iallocbt_calc_size(mp, icount /
261 XFS_INODES_PER_CHUNK);
262 if (xfs_sb_version_hasfinobt(&mp->m_sb))
263 inobt_sz *= 2;
264 if (xfs_sb_version_hasreflink(&mp->m_sb))
265 refcbt_sz = xfs_refcountbt_calc_size(mp, usedlen);
266 else
267 refcbt_sz = 0;
268 if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
269 /*
270 * Guess how many blocks we need to rebuild the rmapbt.
271 * For non-reflink filesystems we can't have more records than
272 * used blocks. However, with reflink it's possible to have
273 * more than one rmap record per AG block. We don't know how
274 * many rmaps there could be in the AG, so we start off with
275 * what we hope is an generous over-estimation.
276 */
277 if (xfs_sb_version_hasreflink(&mp->m_sb))
278 rmapbt_sz = xfs_rmapbt_calc_size(mp,
279 (unsigned long long)aglen * 2);
280 else
281 rmapbt_sz = xfs_rmapbt_calc_size(mp, usedlen);
282 } else {
283 rmapbt_sz = 0;
284 }
285
286 trace_xfs_repair_calc_ag_resblks_btsize(mp, sm->sm_agno, bnobt_sz,
287 inobt_sz, rmapbt_sz, refcbt_sz);
288
289 return max(max(bnobt_sz, inobt_sz), max(rmapbt_sz, refcbt_sz));
290}
diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h
index 83170dd3388c..8d181dce6171 100644
--- a/fs/xfs/scrub/repair.h
+++ b/fs/xfs/scrub/repair.h
@@ -32,6 +32,10 @@ static inline int xfs_repair_notsupported(struct xfs_scrub_context *sc)
32int xfs_repair_attempt(struct xfs_inode *ip, struct xfs_scrub_context *sc, 32int xfs_repair_attempt(struct xfs_inode *ip, struct xfs_scrub_context *sc,
33 bool *fixed); 33 bool *fixed);
34void xfs_repair_failure(struct xfs_mount *mp); 34void xfs_repair_failure(struct xfs_mount *mp);
35int xfs_repair_roll_ag_trans(struct xfs_scrub_context *sc);
36bool xfs_repair_ag_has_space(struct xfs_perag *pag, xfs_extlen_t nr_blocks,
37 enum xfs_ag_resv_type type);
38xfs_extlen_t xfs_repair_calc_ag_resblks(struct xfs_scrub_context *sc);
35 39
36/* Metadata repairers */ 40/* Metadata repairers */
37 41
@@ -49,6 +53,14 @@ static inline int xfs_repair_attempt(
49 53
50static inline void xfs_repair_failure(struct xfs_mount *mp) {} 54static inline void xfs_repair_failure(struct xfs_mount *mp) {}
51 55
56static inline xfs_extlen_t
57xfs_repair_calc_ag_resblks(
58 struct xfs_scrub_context *sc)
59{
60 ASSERT(!(sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR));
61 return 0;
62}
63
52#define xfs_repair_probe xfs_repair_notsupported 64#define xfs_repair_probe xfs_repair_notsupported
53 65
54#endif /* CONFIG_XFS_ONLINE_REPAIR */ 66#endif /* CONFIG_XFS_ONLINE_REPAIR */