162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017-2023 Oracle. All Rights Reserved. 462306a36Sopenharmony_ci * Author: Darrick J. Wong <djwong@kernel.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1162306a36Sopenharmony_ci#include "xfs_mount.h" 1262306a36Sopenharmony_ci#include "xfs_btree.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_log_format.h" 1562306a36Sopenharmony_ci#include "xfs_trans.h" 1662306a36Sopenharmony_ci#include "xfs_rtalloc.h" 1762306a36Sopenharmony_ci#include "xfs_bit.h" 1862306a36Sopenharmony_ci#include "xfs_bmap.h" 1962306a36Sopenharmony_ci#include "scrub/scrub.h" 2062306a36Sopenharmony_ci#include "scrub/common.h" 2162306a36Sopenharmony_ci#include "scrub/trace.h" 2262306a36Sopenharmony_ci#include "scrub/xfile.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Realtime Summary 2662306a36Sopenharmony_ci * ================ 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * We check the realtime summary by scanning the realtime bitmap file to create 2962306a36Sopenharmony_ci * a new summary file incore, and then we compare the computed version against 3062306a36Sopenharmony_ci * the ondisk version. We use the 'xfile' functionality to store this 3162306a36Sopenharmony_ci * (potentially large) amount of data in pageable memory. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Set us up to check the rtsummary file. */ 3562306a36Sopenharmony_ciint 3662306a36Sopenharmony_cixchk_setup_rtsummary( 3762306a36Sopenharmony_ci struct xfs_scrub *sc) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct xfs_mount *mp = sc->mp; 4062306a36Sopenharmony_ci char *descr; 4162306a36Sopenharmony_ci int error; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * Create an xfile to construct a new rtsummary file. The xfile allows 4562306a36Sopenharmony_ci * us to avoid pinning kernel memory for this purpose. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci descr = xchk_xfile_descr(sc, "realtime summary file"); 4862306a36Sopenharmony_ci error = xfile_create(descr, mp->m_rsumsize, &sc->xfile); 4962306a36Sopenharmony_ci kfree(descr); 5062306a36Sopenharmony_ci if (error) 5162306a36Sopenharmony_ci return error; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci error = xchk_trans_alloc(sc, 0); 5462306a36Sopenharmony_ci if (error) 5562306a36Sopenharmony_ci return error; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Allocate a memory buffer for the summary comparison. */ 5862306a36Sopenharmony_ci sc->buf = kvmalloc(mp->m_sb.sb_blocksize, XCHK_GFP_FLAGS); 5962306a36Sopenharmony_ci if (!sc->buf) 6062306a36Sopenharmony_ci return -ENOMEM; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci error = xchk_install_live_inode(sc, mp->m_rsumip); 6362306a36Sopenharmony_ci if (error) 6462306a36Sopenharmony_ci return error; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * Locking order requires us to take the rtbitmap first. We must be 6862306a36Sopenharmony_ci * careful to unlock it ourselves when we are done with the rtbitmap 6962306a36Sopenharmony_ci * file since the scrub infrastructure won't do that for us. Only 7062306a36Sopenharmony_ci * then we can lock the rtsummary inode. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci xfs_ilock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP); 7362306a36Sopenharmony_ci xchk_ilock(sc, XFS_ILOCK_EXCL | XFS_ILOCK_RTSUM); 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* Helper functions to record suminfo words in an xfile. */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_citypedef unsigned int xchk_rtsumoff_t; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic inline int 8262306a36Sopenharmony_cixfsum_load( 8362306a36Sopenharmony_ci struct xfs_scrub *sc, 8462306a36Sopenharmony_ci xchk_rtsumoff_t sumoff, 8562306a36Sopenharmony_ci xfs_suminfo_t *info) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return xfile_obj_load(sc->xfile, info, sizeof(xfs_suminfo_t), 8862306a36Sopenharmony_ci sumoff << XFS_WORDLOG); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic inline int 9262306a36Sopenharmony_cixfsum_store( 9362306a36Sopenharmony_ci struct xfs_scrub *sc, 9462306a36Sopenharmony_ci xchk_rtsumoff_t sumoff, 9562306a36Sopenharmony_ci const xfs_suminfo_t info) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return xfile_obj_store(sc->xfile, &info, sizeof(xfs_suminfo_t), 9862306a36Sopenharmony_ci sumoff << XFS_WORDLOG); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic inline int 10262306a36Sopenharmony_cixfsum_copyout( 10362306a36Sopenharmony_ci struct xfs_scrub *sc, 10462306a36Sopenharmony_ci xchk_rtsumoff_t sumoff, 10562306a36Sopenharmony_ci xfs_suminfo_t *info, 10662306a36Sopenharmony_ci unsigned int nr_words) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return xfile_obj_load(sc->xfile, info, nr_words << XFS_WORDLOG, 10962306a36Sopenharmony_ci sumoff << XFS_WORDLOG); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* Update the summary file to reflect the free extent that we've accumulated. */ 11362306a36Sopenharmony_ciSTATIC int 11462306a36Sopenharmony_cixchk_rtsum_record_free( 11562306a36Sopenharmony_ci struct xfs_mount *mp, 11662306a36Sopenharmony_ci struct xfs_trans *tp, 11762306a36Sopenharmony_ci const struct xfs_rtalloc_rec *rec, 11862306a36Sopenharmony_ci void *priv) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct xfs_scrub *sc = priv; 12162306a36Sopenharmony_ci xfs_fileoff_t rbmoff; 12262306a36Sopenharmony_ci xfs_rtblock_t rtbno; 12362306a36Sopenharmony_ci xfs_filblks_t rtlen; 12462306a36Sopenharmony_ci xchk_rtsumoff_t offs; 12562306a36Sopenharmony_ci unsigned int lenlog; 12662306a36Sopenharmony_ci xfs_suminfo_t v = 0; 12762306a36Sopenharmony_ci int error = 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (xchk_should_terminate(sc, &error)) 13062306a36Sopenharmony_ci return error; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Compute the relevant location in the rtsum file. */ 13362306a36Sopenharmony_ci rbmoff = XFS_BITTOBLOCK(mp, rec->ar_startext); 13462306a36Sopenharmony_ci lenlog = XFS_RTBLOCKLOG(rec->ar_extcount); 13562306a36Sopenharmony_ci offs = XFS_SUMOFFS(mp, lenlog, rbmoff); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci rtbno = rec->ar_startext * mp->m_sb.sb_rextsize; 13862306a36Sopenharmony_ci rtlen = rec->ar_extcount * mp->m_sb.sb_rextsize; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!xfs_verify_rtext(mp, rtbno, rtlen)) { 14162306a36Sopenharmony_ci xchk_ino_xref_set_corrupt(sc, mp->m_rbmip->i_ino); 14262306a36Sopenharmony_ci return -EFSCORRUPTED; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Bump the summary count. */ 14662306a36Sopenharmony_ci error = xfsum_load(sc, offs, &v); 14762306a36Sopenharmony_ci if (error) 14862306a36Sopenharmony_ci return error; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci v++; 15162306a36Sopenharmony_ci trace_xchk_rtsum_record_free(mp, rec->ar_startext, rec->ar_extcount, 15262306a36Sopenharmony_ci lenlog, offs, v); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return xfsum_store(sc, offs, v); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* Compute the realtime summary from the realtime bitmap. */ 15862306a36Sopenharmony_ciSTATIC int 15962306a36Sopenharmony_cixchk_rtsum_compute( 16062306a36Sopenharmony_ci struct xfs_scrub *sc) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct xfs_mount *mp = sc->mp; 16362306a36Sopenharmony_ci unsigned long long rtbmp_bytes; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* If the bitmap size doesn't match the computed size, bail. */ 16662306a36Sopenharmony_ci rtbmp_bytes = howmany_64(mp->m_sb.sb_rextents, NBBY); 16762306a36Sopenharmony_ci if (roundup_64(rtbmp_bytes, mp->m_sb.sb_blocksize) != 16862306a36Sopenharmony_ci mp->m_rbmip->i_disk_size) 16962306a36Sopenharmony_ci return -EFSCORRUPTED; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return xfs_rtalloc_query_all(sc->mp, sc->tp, xchk_rtsum_record_free, 17262306a36Sopenharmony_ci sc); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* Compare the rtsummary file against the one we computed. */ 17662306a36Sopenharmony_ciSTATIC int 17762306a36Sopenharmony_cixchk_rtsum_compare( 17862306a36Sopenharmony_ci struct xfs_scrub *sc) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct xfs_mount *mp = sc->mp; 18162306a36Sopenharmony_ci struct xfs_buf *bp; 18262306a36Sopenharmony_ci struct xfs_bmbt_irec map; 18362306a36Sopenharmony_ci xfs_fileoff_t off; 18462306a36Sopenharmony_ci xchk_rtsumoff_t sumoff = 0; 18562306a36Sopenharmony_ci int nmap; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci for (off = 0; off < XFS_B_TO_FSB(mp, mp->m_rsumsize); off++) { 18862306a36Sopenharmony_ci int error = 0; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (xchk_should_terminate(sc, &error)) 19162306a36Sopenharmony_ci return error; 19262306a36Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Make sure we have a written extent. */ 19662306a36Sopenharmony_ci nmap = 1; 19762306a36Sopenharmony_ci error = xfs_bmapi_read(mp->m_rsumip, off, 1, &map, &nmap, 19862306a36Sopenharmony_ci XFS_DATA_FORK); 19962306a36Sopenharmony_ci if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, off, &error)) 20062306a36Sopenharmony_ci return error; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (nmap != 1 || !xfs_bmap_is_written_extent(&map)) { 20362306a36Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, off); 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Read a block's worth of ondisk rtsummary file. */ 20862306a36Sopenharmony_ci error = xfs_rtbuf_get(mp, sc->tp, off, 1, &bp); 20962306a36Sopenharmony_ci if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, off, &error)) 21062306a36Sopenharmony_ci return error; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Read a block's worth of computed rtsummary file. */ 21362306a36Sopenharmony_ci error = xfsum_copyout(sc, sumoff, sc->buf, mp->m_blockwsize); 21462306a36Sopenharmony_ci if (error) { 21562306a36Sopenharmony_ci xfs_trans_brelse(sc->tp, bp); 21662306a36Sopenharmony_ci return error; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (memcmp(bp->b_addr, sc->buf, 22062306a36Sopenharmony_ci mp->m_blockwsize << XFS_WORDLOG) != 0) 22162306a36Sopenharmony_ci xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, off); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci xfs_trans_brelse(sc->tp, bp); 22462306a36Sopenharmony_ci sumoff += mp->m_blockwsize; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* Scrub the realtime summary. */ 23162306a36Sopenharmony_ciint 23262306a36Sopenharmony_cixchk_rtsummary( 23362306a36Sopenharmony_ci struct xfs_scrub *sc) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct xfs_mount *mp = sc->mp; 23662306a36Sopenharmony_ci int error = 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Invoke the fork scrubber. */ 23962306a36Sopenharmony_ci error = xchk_metadata_inode_forks(sc); 24062306a36Sopenharmony_ci if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 24162306a36Sopenharmony_ci goto out_rbm; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* Construct the new summary file from the rtbitmap. */ 24462306a36Sopenharmony_ci error = xchk_rtsum_compute(sc); 24562306a36Sopenharmony_ci if (error == -EFSCORRUPTED) { 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * EFSCORRUPTED means the rtbitmap is corrupt, which is an xref 24862306a36Sopenharmony_ci * error since we're checking the summary file. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci xchk_ino_xref_set_corrupt(sc, mp->m_rbmip->i_ino); 25162306a36Sopenharmony_ci error = 0; 25262306a36Sopenharmony_ci goto out_rbm; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci if (error) 25562306a36Sopenharmony_ci goto out_rbm; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Does the computed summary file match the actual rtsummary file? */ 25862306a36Sopenharmony_ci error = xchk_rtsum_compare(sc); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciout_rbm: 26162306a36Sopenharmony_ci /* Unlock the rtbitmap since we're done with it. */ 26262306a36Sopenharmony_ci xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP); 26362306a36Sopenharmony_ci return error; 26462306a36Sopenharmony_ci} 265