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