xref: /linux/fs/xfs/scrub/rtsummary.c (revision 705c09bb3cdffb141986598ad4ff9c9b0a66c3bd)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2017-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_inode.h"
14 #include "xfs_log_format.h"
15 #include "xfs_trans.h"
16 #include "xfs_rtbitmap.h"
17 #include "xfs_bit.h"
18 #include "xfs_bmap.h"
19 #include "xfs_sb.h"
20 #include "scrub/scrub.h"
21 #include "scrub/common.h"
22 #include "scrub/trace.h"
23 #include "scrub/xfile.h"
24 
25 /*
26  * Realtime Summary
27  * ================
28  *
29  * We check the realtime summary by scanning the realtime bitmap file to create
30  * a new summary file incore, and then we compare the computed version against
31  * the ondisk version.  We use the 'xfile' functionality to store this
32  * (potentially large) amount of data in pageable memory.
33  */
34 
35 struct xchk_rtsummary {
36 	struct xfs_rtalloc_args	args;
37 
38 	uint64_t		rextents;
39 	uint64_t		rbmblocks;
40 	uint64_t		rsumsize;
41 	unsigned int		rsumlevels;
42 
43 	/* Memory buffer for the summary comparison. */
44 	union xfs_suminfo_raw	words[];
45 };
46 
47 /* Set us up to check the rtsummary file. */
48 int
49 xchk_setup_rtsummary(
50 	struct xfs_scrub	*sc)
51 {
52 	struct xfs_mount	*mp = sc->mp;
53 	char			*descr;
54 	struct xchk_rtsummary	*rts;
55 	int			error;
56 
57 	rts = kvzalloc(struct_size(rts, words, mp->m_blockwsize),
58 			XCHK_GFP_FLAGS);
59 	if (!rts)
60 		return -ENOMEM;
61 	sc->buf = rts;
62 
63 	/*
64 	 * Create an xfile to construct a new rtsummary file.  The xfile allows
65 	 * us to avoid pinning kernel memory for this purpose.
66 	 */
67 	descr = xchk_xfile_descr(sc, "realtime summary file");
68 	error = xfile_create(descr, mp->m_rsumsize, &sc->xfile);
69 	kfree(descr);
70 	if (error)
71 		return error;
72 
73 	error = xchk_trans_alloc(sc, 0);
74 	if (error)
75 		return error;
76 
77 	error = xchk_install_live_inode(sc, mp->m_rsumip);
78 	if (error)
79 		return error;
80 
81 	error = xchk_ino_dqattach(sc);
82 	if (error)
83 		return error;
84 
85 	/*
86 	 * Locking order requires us to take the rtbitmap first.  We must be
87 	 * careful to unlock it ourselves when we are done with the rtbitmap
88 	 * file since the scrub infrastructure won't do that for us.  Only
89 	 * then we can lock the rtsummary inode.
90 	 */
91 	xfs_ilock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
92 	xchk_ilock(sc, XFS_ILOCK_EXCL | XFS_ILOCK_RTSUM);
93 
94 	/*
95 	 * Now that we've locked the rtbitmap and rtsummary, we can't race with
96 	 * growfsrt trying to expand the summary or change the size of the rt
97 	 * volume.  Hence it is safe to compute and check the geometry values.
98 	 */
99 	if (mp->m_sb.sb_rblocks) {
100 		xfs_filblks_t	rsumblocks;
101 		int		rextslog;
102 
103 		rts->rextents = xfs_rtb_to_rtx(mp, mp->m_sb.sb_rblocks);
104 		rextslog = xfs_compute_rextslog(rts->rextents);
105 		rts->rsumlevels = rextslog + 1;
106 		rts->rbmblocks = xfs_rtbitmap_blockcount(mp, rts->rextents);
107 		rsumblocks = xfs_rtsummary_blockcount(mp, rts->rsumlevels,
108 				rts->rbmblocks);
109 		rts->rsumsize = XFS_FSB_TO_B(mp, rsumblocks);
110 	}
111 	return 0;
112 }
113 
114 /* Helper functions to record suminfo words in an xfile. */
115 
116 static inline int
117 xfsum_load(
118 	struct xfs_scrub	*sc,
119 	xfs_rtsumoff_t		sumoff,
120 	union xfs_suminfo_raw	*rawinfo)
121 {
122 	return xfile_load(sc->xfile, rawinfo,
123 			sizeof(union xfs_suminfo_raw),
124 			sumoff << XFS_WORDLOG);
125 }
126 
127 static inline int
128 xfsum_store(
129 	struct xfs_scrub	*sc,
130 	xfs_rtsumoff_t		sumoff,
131 	const union xfs_suminfo_raw rawinfo)
132 {
133 	return xfile_store(sc->xfile, &rawinfo,
134 			sizeof(union xfs_suminfo_raw),
135 			sumoff << XFS_WORDLOG);
136 }
137 
138 static inline int
139 xfsum_copyout(
140 	struct xfs_scrub	*sc,
141 	xfs_rtsumoff_t		sumoff,
142 	union xfs_suminfo_raw	*rawinfo,
143 	unsigned int		nr_words)
144 {
145 	return xfile_load(sc->xfile, rawinfo, nr_words << XFS_WORDLOG,
146 			sumoff << XFS_WORDLOG);
147 }
148 
149 static inline xfs_suminfo_t
150 xchk_rtsum_inc(
151 	struct xfs_mount	*mp,
152 	union xfs_suminfo_raw	*v)
153 {
154 	v->old += 1;
155 	return v->old;
156 }
157 
158 /* Update the summary file to reflect the free extent that we've accumulated. */
159 STATIC int
160 xchk_rtsum_record_free(
161 	struct xfs_mount		*mp,
162 	struct xfs_trans		*tp,
163 	const struct xfs_rtalloc_rec	*rec,
164 	void				*priv)
165 {
166 	struct xfs_scrub		*sc = priv;
167 	xfs_fileoff_t			rbmoff;
168 	xfs_rtblock_t			rtbno;
169 	xfs_filblks_t			rtlen;
170 	xfs_rtsumoff_t			offs;
171 	unsigned int			lenlog;
172 	union xfs_suminfo_raw		v;
173 	xfs_suminfo_t			value;
174 	int				error = 0;
175 
176 	if (xchk_should_terminate(sc, &error))
177 		return error;
178 
179 	/* Compute the relevant location in the rtsum file. */
180 	rbmoff = xfs_rtx_to_rbmblock(mp, rec->ar_startext);
181 	lenlog = xfs_highbit64(rec->ar_extcount);
182 	offs = xfs_rtsumoffs(mp, lenlog, rbmoff);
183 
184 	rtbno = xfs_rtx_to_rtb(mp, rec->ar_startext);
185 	rtlen = xfs_rtx_to_rtb(mp, rec->ar_extcount);
186 
187 	if (!xfs_verify_rtbext(mp, rtbno, rtlen)) {
188 		xchk_ino_xref_set_corrupt(sc, mp->m_rbmip->i_ino);
189 		return -EFSCORRUPTED;
190 	}
191 
192 	/* Bump the summary count. */
193 	error = xfsum_load(sc, offs, &v);
194 	if (error)
195 		return error;
196 
197 	value = xchk_rtsum_inc(sc->mp, &v);
198 	trace_xchk_rtsum_record_free(mp, rec->ar_startext, rec->ar_extcount,
199 			lenlog, offs, value);
200 
201 	return xfsum_store(sc, offs, v);
202 }
203 
204 /* Compute the realtime summary from the realtime bitmap. */
205 STATIC int
206 xchk_rtsum_compute(
207 	struct xfs_scrub	*sc)
208 {
209 	struct xfs_mount	*mp = sc->mp;
210 	unsigned long long	rtbmp_blocks;
211 
212 	/* If the bitmap size doesn't match the computed size, bail. */
213 	rtbmp_blocks = xfs_rtbitmap_blockcount(mp, mp->m_sb.sb_rextents);
214 	if (XFS_FSB_TO_B(mp, rtbmp_blocks) != mp->m_rbmip->i_disk_size)
215 		return -EFSCORRUPTED;
216 
217 	return xfs_rtalloc_query_all(sc->mp, sc->tp, xchk_rtsum_record_free,
218 			sc);
219 }
220 
221 /* Compare the rtsummary file against the one we computed. */
222 STATIC int
223 xchk_rtsum_compare(
224 	struct xfs_scrub	*sc)
225 {
226 	struct xfs_bmbt_irec	map;
227 	struct xfs_iext_cursor	icur;
228 
229 	struct xfs_mount	*mp = sc->mp;
230 	struct xfs_inode	*ip = sc->ip;
231 	struct xchk_rtsummary	*rts = sc->buf;
232 	xfs_fileoff_t		off = 0;
233 	xfs_fileoff_t		endoff;
234 	xfs_rtsumoff_t		sumoff = 0;
235 	int			error = 0;
236 
237 	rts->args.mp = sc->mp;
238 	rts->args.tp = sc->tp;
239 
240 	/* Mappings may not cross or lie beyond EOF. */
241 	endoff = XFS_B_TO_FSB(mp, ip->i_disk_size);
242 	if (xfs_iext_lookup_extent(ip, &ip->i_df, endoff, &icur, &map)) {
243 		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, endoff);
244 		return 0;
245 	}
246 
247 	while (off < endoff) {
248 		int		nmap = 1;
249 
250 		if (xchk_should_terminate(sc, &error))
251 			return error;
252 		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
253 			return 0;
254 
255 		/* Make sure we have a written extent. */
256 		error = xfs_bmapi_read(ip, off, endoff - off, &map, &nmap,
257 				XFS_DATA_FORK);
258 		if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, off, &error))
259 			return error;
260 
261 		if (nmap != 1 || !xfs_bmap_is_written_extent(&map)) {
262 			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, off);
263 			return 0;
264 		}
265 
266 		off += map.br_blockcount;
267 	}
268 
269 	for (off = 0; off < endoff; off++) {
270 		union xfs_suminfo_raw	*ondisk_info;
271 
272 		/* Read a block's worth of ondisk rtsummary file. */
273 		error = xfs_rtsummary_read_buf(&rts->args, off);
274 		if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, off, &error))
275 			return error;
276 
277 		/* Read a block's worth of computed rtsummary file. */
278 		error = xfsum_copyout(sc, sumoff, rts->words, mp->m_blockwsize);
279 		if (error) {
280 			xfs_rtbuf_cache_relse(&rts->args);
281 			return error;
282 		}
283 
284 		ondisk_info = xfs_rsumblock_infoptr(&rts->args, 0);
285 		if (memcmp(ondisk_info, rts->words,
286 					mp->m_blockwsize << XFS_WORDLOG) != 0) {
287 			xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, off);
288 			xfs_rtbuf_cache_relse(&rts->args);
289 			return error;
290 		}
291 
292 		xfs_rtbuf_cache_relse(&rts->args);
293 		sumoff += mp->m_blockwsize;
294 	}
295 
296 	return 0;
297 }
298 
299 /* Scrub the realtime summary. */
300 int
301 xchk_rtsummary(
302 	struct xfs_scrub	*sc)
303 {
304 	struct xfs_mount	*mp = sc->mp;
305 	struct xchk_rtsummary	*rts = sc->buf;
306 	int			error = 0;
307 
308 	/* Is sb_rextents correct? */
309 	if (mp->m_sb.sb_rextents != rts->rextents) {
310 		xchk_ino_set_corrupt(sc, mp->m_rbmip->i_ino);
311 		goto out_rbm;
312 	}
313 
314 	/* Is m_rsumlevels correct? */
315 	if (mp->m_rsumlevels != rts->rsumlevels) {
316 		xchk_ino_set_corrupt(sc, mp->m_rsumip->i_ino);
317 		goto out_rbm;
318 	}
319 
320 	/* Is m_rsumsize correct? */
321 	if (mp->m_rsumsize != rts->rsumsize) {
322 		xchk_ino_set_corrupt(sc, mp->m_rsumip->i_ino);
323 		goto out_rbm;
324 	}
325 
326 	/* The summary file length must be aligned to an fsblock. */
327 	if (mp->m_rsumip->i_disk_size & mp->m_blockmask) {
328 		xchk_ino_set_corrupt(sc, mp->m_rsumip->i_ino);
329 		goto out_rbm;
330 	}
331 
332 	/*
333 	 * Is the summary file itself large enough to handle the rt volume?
334 	 * growfsrt expands the summary file before updating sb_rextents, so
335 	 * the file can be larger than rsumsize.
336 	 */
337 	if (mp->m_rsumip->i_disk_size < rts->rsumsize) {
338 		xchk_ino_set_corrupt(sc, mp->m_rsumip->i_ino);
339 		goto out_rbm;
340 	}
341 
342 	/* Invoke the fork scrubber. */
343 	error = xchk_metadata_inode_forks(sc);
344 	if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
345 		goto out_rbm;
346 
347 	/* Construct the new summary file from the rtbitmap. */
348 	error = xchk_rtsum_compute(sc);
349 	if (error == -EFSCORRUPTED) {
350 		/*
351 		 * EFSCORRUPTED means the rtbitmap is corrupt, which is an xref
352 		 * error since we're checking the summary file.
353 		 */
354 		xchk_ino_xref_set_corrupt(sc, mp->m_rbmip->i_ino);
355 		error = 0;
356 		goto out_rbm;
357 	}
358 	if (error)
359 		goto out_rbm;
360 
361 	/* Does the computed summary file match the actual rtsummary file? */
362 	error = xchk_rtsum_compare(sc);
363 
364 out_rbm:
365 	/* Unlock the rtbitmap since we're done with it. */
366 	xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
367 	return error;
368 }
369