Skip to content

Commit e06ef14

Browse files
author
Darrick J. Wong
committed
xfs: move the post-repair block reaping code to a separate file
Reaping blocks after a repair is a complicated affair involving a lot of rmap btree lookups and figuring out if we're going to unmap or free old metadata blocks that might be crosslinked. Eventually, we will need to be able to reap per-AG metadata blocks, bmbt blocks from inode forks, garbage CoW staging extents, and (even later) blocks from btrees rooted in inodes. This results in a lot of reaping code, so we might as well split that off while it's easy. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent 86a4641 commit e06ef14

6 files changed

Lines changed: 283 additions & 234 deletions

File tree

fs/xfs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o
173173
ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y)
174174
xfs-y += $(addprefix scrub/, \
175175
agheader_repair.o \
176+
reap.o \
176177
repair.o \
177178
)
178179
endif

fs/xfs/scrub/agheader_repair.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "scrub/trace.h"
2727
#include "scrub/repair.h"
2828
#include "scrub/bitmap.h"
29+
#include "scrub/reap.h"
2930

3031
/* Superblock */
3132

fs/xfs/scrub/reap.c

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2022-2023 Oracle. All Rights Reserved.
4+
* Author: Darrick J. Wong <djwong@kernel.org>
5+
*/
6+
#include "xfs.h"
7+
#include "xfs_fs.h"
8+
#include "xfs_shared.h"
9+
#include "xfs_format.h"
10+
#include "xfs_trans_resv.h"
11+
#include "xfs_mount.h"
12+
#include "xfs_btree.h"
13+
#include "xfs_log_format.h"
14+
#include "xfs_trans.h"
15+
#include "xfs_sb.h"
16+
#include "xfs_inode.h"
17+
#include "xfs_alloc.h"
18+
#include "xfs_alloc_btree.h"
19+
#include "xfs_ialloc.h"
20+
#include "xfs_ialloc_btree.h"
21+
#include "xfs_rmap.h"
22+
#include "xfs_rmap_btree.h"
23+
#include "xfs_refcount_btree.h"
24+
#include "xfs_extent_busy.h"
25+
#include "xfs_ag.h"
26+
#include "xfs_ag_resv.h"
27+
#include "xfs_quota.h"
28+
#include "xfs_qm.h"
29+
#include "scrub/scrub.h"
30+
#include "scrub/common.h"
31+
#include "scrub/trace.h"
32+
#include "scrub/repair.h"
33+
#include "scrub/bitmap.h"
34+
#include "scrub/reap.h"
35+
36+
/*
37+
* Disposal of Blocks from Old Metadata
38+
*
39+
* Now that we've constructed a new btree to replace the damaged one, we want
40+
* to dispose of the blocks that (we think) the old btree was using.
41+
* Previously, we used the rmapbt to collect the extents (bitmap) with the
42+
* rmap owner corresponding to the tree we rebuilt, collected extents for any
43+
* blocks with the same rmap owner that are owned by another data structure
44+
* (sublist), and subtracted sublist from bitmap. In theory the extents
45+
* remaining in bitmap are the old btree's blocks.
46+
*
47+
* Unfortunately, it's possible that the btree was crosslinked with other
48+
* blocks on disk. The rmap data can tell us if there are multiple owners, so
49+
* if the rmapbt says there is an owner of this block other than @oinfo, then
50+
* the block is crosslinked. Remove the reverse mapping and continue.
51+
*
52+
* If there is one rmap record, we can free the block, which removes the
53+
* reverse mapping but doesn't add the block to the free space. Our repair
54+
* strategy is to hope the other metadata objects crosslinked on this block
55+
* will be rebuilt (atop different blocks), thereby removing all the cross
56+
* links.
57+
*
58+
* If there are no rmap records at all, we also free the block. If the btree
59+
* being rebuilt lives in the free space (bnobt/cntbt/rmapbt) then there isn't
60+
* supposed to be a rmap record and everything is ok. For other btrees there
61+
* had to have been an rmap entry for the block to have ended up on @bitmap,
62+
* so if it's gone now there's something wrong and the fs will shut down.
63+
*
64+
* Note: If there are multiple rmap records with only the same rmap owner as
65+
* the btree we're trying to rebuild and the block is indeed owned by another
66+
* data structure with the same rmap owner, then the block will be in sublist
67+
* and therefore doesn't need disposal. If there are multiple rmap records
68+
* with only the same rmap owner but the block is not owned by something with
69+
* the same rmap owner, the block will be freed.
70+
*
71+
* The caller is responsible for locking the AG headers for the entire rebuild
72+
* operation so that nothing else can sneak in and change the AG state while
73+
* we're not looking. We also assume that the caller already invalidated any
74+
* buffers associated with @bitmap.
75+
*/
76+
77+
static int
78+
xrep_invalidate_block(
79+
uint64_t fsbno,
80+
void *priv)
81+
{
82+
struct xfs_scrub *sc = priv;
83+
struct xfs_buf *bp;
84+
int error;
85+
86+
/* Skip AG headers and post-EOFS blocks */
87+
if (!xfs_verify_fsbno(sc->mp, fsbno))
88+
return 0;
89+
90+
error = xfs_buf_incore(sc->mp->m_ddev_targp,
91+
XFS_FSB_TO_DADDR(sc->mp, fsbno),
92+
XFS_FSB_TO_BB(sc->mp, 1), XBF_TRYLOCK, &bp);
93+
if (error)
94+
return 0;
95+
96+
xfs_trans_bjoin(sc->tp, bp);
97+
xfs_trans_binval(sc->tp, bp);
98+
return 0;
99+
}
100+
101+
/*
102+
* Invalidate buffers for per-AG btree blocks we're dumping. This function
103+
* is not intended for use with file data repairs; we have bunmapi for that.
104+
*/
105+
int
106+
xrep_invalidate_blocks(
107+
struct xfs_scrub *sc,
108+
struct xbitmap *bitmap)
109+
{
110+
/*
111+
* For each block in each extent, see if there's an incore buffer for
112+
* exactly that block; if so, invalidate it. The buffer cache only
113+
* lets us look for one buffer at a time, so we have to look one block
114+
* at a time. Avoid invalidating AG headers and post-EOFS blocks
115+
* because we never own those; and if we can't TRYLOCK the buffer we
116+
* assume it's owned by someone else.
117+
*/
118+
return xbitmap_walk_bits(bitmap, xrep_invalidate_block, sc);
119+
}
120+
121+
/* Information about reaping extents after a repair. */
122+
struct xrep_reap_state {
123+
struct xfs_scrub *sc;
124+
125+
/* Reverse mapping owner and metadata reservation type. */
126+
const struct xfs_owner_info *oinfo;
127+
enum xfs_ag_resv_type resv;
128+
};
129+
130+
/*
131+
* Put a block back on the AGFL.
132+
*/
133+
STATIC int
134+
xrep_put_freelist(
135+
struct xfs_scrub *sc,
136+
xfs_agblock_t agbno)
137+
{
138+
struct xfs_buf *agfl_bp;
139+
int error;
140+
141+
/* Make sure there's space on the freelist. */
142+
error = xrep_fix_freelist(sc, true);
143+
if (error)
144+
return error;
145+
146+
/*
147+
* Since we're "freeing" a lost block onto the AGFL, we have to
148+
* create an rmap for the block prior to merging it or else other
149+
* parts will break.
150+
*/
151+
error = xfs_rmap_alloc(sc->tp, sc->sa.agf_bp, sc->sa.pag, agbno, 1,
152+
&XFS_RMAP_OINFO_AG);
153+
if (error)
154+
return error;
155+
156+
/* Put the block on the AGFL. */
157+
error = xfs_alloc_read_agfl(sc->sa.pag, sc->tp, &agfl_bp);
158+
if (error)
159+
return error;
160+
161+
error = xfs_alloc_put_freelist(sc->sa.pag, sc->tp, sc->sa.agf_bp,
162+
agfl_bp, agbno, 0);
163+
if (error)
164+
return error;
165+
xfs_extent_busy_insert(sc->tp, sc->sa.pag, agbno, 1,
166+
XFS_EXTENT_BUSY_SKIP_DISCARD);
167+
168+
return 0;
169+
}
170+
171+
/* Dispose of a single block. */
172+
STATIC int
173+
xrep_reap_block(
174+
uint64_t fsbno,
175+
void *priv)
176+
{
177+
struct xrep_reap_state *rs = priv;
178+
struct xfs_scrub *sc = rs->sc;
179+
struct xfs_btree_cur *cur;
180+
struct xfs_buf *agf_bp = NULL;
181+
xfs_agblock_t agbno;
182+
bool has_other_rmap;
183+
int error;
184+
185+
ASSERT(sc->ip != NULL ||
186+
XFS_FSB_TO_AGNO(sc->mp, fsbno) == sc->sa.pag->pag_agno);
187+
trace_xrep_dispose_btree_extent(sc->mp,
188+
XFS_FSB_TO_AGNO(sc->mp, fsbno),
189+
XFS_FSB_TO_AGBNO(sc->mp, fsbno), 1);
190+
191+
agbno = XFS_FSB_TO_AGBNO(sc->mp, fsbno);
192+
ASSERT(XFS_FSB_TO_AGNO(sc->mp, fsbno) == sc->sa.pag->pag_agno);
193+
194+
/*
195+
* If we are repairing per-inode metadata, we need to read in the AGF
196+
* buffer. Otherwise, we're repairing a per-AG structure, so reuse
197+
* the AGF buffer that the setup functions already grabbed.
198+
*/
199+
if (sc->ip) {
200+
error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &agf_bp);
201+
if (error)
202+
return error;
203+
} else {
204+
agf_bp = sc->sa.agf_bp;
205+
}
206+
cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf_bp, sc->sa.pag);
207+
208+
/* Can we find any other rmappings? */
209+
error = xfs_rmap_has_other_keys(cur, agbno, 1, rs->oinfo,
210+
&has_other_rmap);
211+
xfs_btree_del_cursor(cur, error);
212+
if (error)
213+
goto out_free;
214+
215+
/*
216+
* If there are other rmappings, this block is cross linked and must
217+
* not be freed. Remove the reverse mapping and move on. Otherwise,
218+
* we were the only owner of the block, so free the extent, which will
219+
* also remove the rmap.
220+
*
221+
* XXX: XFS doesn't support detecting the case where a single block
222+
* metadata structure is crosslinked with a multi-block structure
223+
* because the buffer cache doesn't detect aliasing problems, so we
224+
* can't fix 100% of crosslinking problems (yet). The verifiers will
225+
* blow on writeout, the filesystem will shut down, and the admin gets
226+
* to run xfs_repair.
227+
*/
228+
if (has_other_rmap)
229+
error = xfs_rmap_free(sc->tp, agf_bp, sc->sa.pag, agbno,
230+
1, rs->oinfo);
231+
else if (rs->resv == XFS_AG_RESV_AGFL)
232+
error = xrep_put_freelist(sc, agbno);
233+
else
234+
error = xfs_free_extent(sc->tp, sc->sa.pag, agbno, 1, rs->oinfo,
235+
rs->resv);
236+
if (agf_bp != sc->sa.agf_bp)
237+
xfs_trans_brelse(sc->tp, agf_bp);
238+
if (error)
239+
return error;
240+
241+
if (sc->ip)
242+
return xfs_trans_roll_inode(&sc->tp, sc->ip);
243+
return xrep_roll_ag_trans(sc);
244+
245+
out_free:
246+
if (agf_bp != sc->sa.agf_bp)
247+
xfs_trans_brelse(sc->tp, agf_bp);
248+
return error;
249+
}
250+
251+
/* Dispose of every block of every extent in the bitmap. */
252+
int
253+
xrep_reap_extents(
254+
struct xfs_scrub *sc,
255+
struct xbitmap *bitmap,
256+
const struct xfs_owner_info *oinfo,
257+
enum xfs_ag_resv_type type)
258+
{
259+
struct xrep_reap_state rs = {
260+
.sc = sc,
261+
.oinfo = oinfo,
262+
.resv = type,
263+
};
264+
265+
ASSERT(xfs_has_rmapbt(sc->mp));
266+
267+
return xbitmap_walk_bits(bitmap, xrep_reap_block, &rs);
268+
}

fs/xfs/scrub/reap.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2022-2023 Oracle. All Rights Reserved.
4+
* Author: Darrick J. Wong <djwong@kernel.org>
5+
*/
6+
#ifndef __XFS_SCRUB_REAP_H__
7+
#define __XFS_SCRUB_REAP_H__
8+
9+
int xrep_reap_extents(struct xfs_scrub *sc, struct xbitmap *bitmap,
10+
const struct xfs_owner_info *oinfo,
11+
enum xfs_ag_resv_type type);
12+
13+
#endif /* __XFS_SCRUB_REAP_H__ */

0 commit comments

Comments
 (0)