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 "scrub/scrub.h" 20 #include "scrub/common.h" 21 #include "scrub/trace.h" 22 #include "scrub/xfile.h" 23 24 /* 25 * Realtime Summary 26 * ================ 27 * 28 * We check the realtime summary by scanning the realtime bitmap file to create 29 * a new summary file incore, and then we compare the computed version against 30 * the ondisk version. We use the 'xfile' functionality to store this 31 * (potentially large) amount of data in pageable memory. 32 */ 33 34 /* Set us up to check the rtsummary file. */ 35 int 36 xchk_setup_rtsummary( 37 struct xfs_scrub *sc) 38 { 39 struct xfs_mount *mp = sc->mp; 40 char *descr; 41 int error; 42 43 /* 44 * Create an xfile to construct a new rtsummary file. The xfile allows 45 * us to avoid pinning kernel memory for this purpose. 46 */ 47 descr = xchk_xfile_descr(sc, "realtime summary file"); 48 error = xfile_create(descr, mp->m_rsumsize, &sc->xfile); 49 kfree(descr); 50 if (error) 51 return error; 52 53 error = xchk_trans_alloc(sc, 0); 54 if (error) 55 return error; 56 57 /* Allocate a memory buffer for the summary comparison. */ 58 sc->buf = kvmalloc(mp->m_sb.sb_blocksize, XCHK_GFP_FLAGS); 59 if (!sc->buf) 60 return -ENOMEM; 61 62 error = xchk_install_live_inode(sc, mp->m_rsumip); 63 if (error) 64 return error; 65 66 /* 67 * Locking order requires us to take the rtbitmap first. We must be 68 * careful to unlock it ourselves when we are done with the rtbitmap 69 * file since the scrub infrastructure won't do that for us. Only 70 * then we can lock the rtsummary inode. 71 */ 72 xfs_ilock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP); 73 xchk_ilock(sc, XFS_ILOCK_EXCL | XFS_ILOCK_RTSUM); 74 return 0; 75 } 76 77 /* Helper functions to record suminfo words in an xfile. */ 78 79 typedef unsigned int xchk_rtsumoff_t; 80 81 static inline int 82 xfsum_load( 83 struct xfs_scrub *sc, 84 xfs_rtsumoff_t sumoff, 85 union xfs_suminfo_raw *rawinfo) 86 { 87 return xfile_obj_load(sc->xfile, rawinfo, 88 sizeof(union xfs_suminfo_raw), 89 sumoff << XFS_WORDLOG); 90 } 91 92 static inline int 93 xfsum_store( 94 struct xfs_scrub *sc, 95 xfs_rtsumoff_t sumoff, 96 const union xfs_suminfo_raw rawinfo) 97 { 98 return xfile_obj_store(sc->xfile, &rawinfo, 99 sizeof(union xfs_suminfo_raw), 100 sumoff << XFS_WORDLOG); 101 } 102 103 static inline int 104 xfsum_copyout( 105 struct xfs_scrub *sc, 106 xfs_rtsumoff_t sumoff, 107 union xfs_suminfo_raw *rawinfo, 108 unsigned int nr_words) 109 { 110 return xfile_obj_load(sc->xfile, rawinfo, nr_words << XFS_WORDLOG, 111 sumoff << XFS_WORDLOG); 112 } 113 114 static inline xfs_suminfo_t 115 xchk_rtsum_inc( 116 struct xfs_mount *mp, 117 union xfs_suminfo_raw *v) 118 { 119 v->old += 1; 120 return v->old; 121 } 122 123 /* Update the summary file to reflect the free extent that we've accumulated. */ 124 STATIC int 125 xchk_rtsum_record_free( 126 struct xfs_mount *mp, 127 struct xfs_trans *tp, 128 const struct xfs_rtalloc_rec *rec, 129 void *priv) 130 { 131 struct xfs_scrub *sc = priv; 132 xfs_fileoff_t rbmoff; 133 xfs_rtblock_t rtbno; 134 xfs_filblks_t rtlen; 135 xfs_rtsumoff_t offs; 136 unsigned int lenlog; 137 union xfs_suminfo_raw v; 138 xfs_suminfo_t value; 139 int error = 0; 140 141 if (xchk_should_terminate(sc, &error)) 142 return error; 143 144 /* Compute the relevant location in the rtsum file. */ 145 rbmoff = xfs_rtx_to_rbmblock(mp, rec->ar_startext); 146 lenlog = XFS_RTBLOCKLOG(rec->ar_extcount); 147 offs = xfs_rtsumoffs(mp, lenlog, rbmoff); 148 149 rtbno = xfs_rtx_to_rtb(mp, rec->ar_startext); 150 rtlen = xfs_rtx_to_rtb(mp, rec->ar_extcount); 151 152 if (!xfs_verify_rtbext(mp, rtbno, rtlen)) { 153 xchk_ino_xref_set_corrupt(sc, mp->m_rbmip->i_ino); 154 return -EFSCORRUPTED; 155 } 156 157 /* Bump the summary count. */ 158 error = xfsum_load(sc, offs, &v); 159 if (error) 160 return error; 161 162 value = xchk_rtsum_inc(sc->mp, &v); 163 trace_xchk_rtsum_record_free(mp, rec->ar_startext, rec->ar_extcount, 164 lenlog, offs, value); 165 166 return xfsum_store(sc, offs, v); 167 } 168 169 /* Compute the realtime summary from the realtime bitmap. */ 170 STATIC int 171 xchk_rtsum_compute( 172 struct xfs_scrub *sc) 173 { 174 struct xfs_mount *mp = sc->mp; 175 unsigned long long rtbmp_blocks; 176 177 /* If the bitmap size doesn't match the computed size, bail. */ 178 rtbmp_blocks = xfs_rtbitmap_blockcount(mp, mp->m_sb.sb_rextents); 179 if (XFS_FSB_TO_B(mp, rtbmp_blocks) != mp->m_rbmip->i_disk_size) 180 return -EFSCORRUPTED; 181 182 return xfs_rtalloc_query_all(sc->mp, sc->tp, xchk_rtsum_record_free, 183 sc); 184 } 185 186 /* Compare the rtsummary file against the one we computed. */ 187 STATIC int 188 xchk_rtsum_compare( 189 struct xfs_scrub *sc) 190 { 191 struct xfs_rtalloc_args args = { 192 .mp = sc->mp, 193 .tp = sc->tp, 194 }; 195 struct xfs_mount *mp = sc->mp; 196 struct xfs_bmbt_irec map; 197 xfs_fileoff_t off; 198 xchk_rtsumoff_t sumoff = 0; 199 int nmap; 200 201 for (off = 0; off < XFS_B_TO_FSB(mp, mp->m_rsumsize); off++) { 202 union xfs_suminfo_raw *ondisk_info; 203 int error = 0; 204 205 if (xchk_should_terminate(sc, &error)) 206 return error; 207 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 208 return 0; 209 210 /* Make sure we have a written extent. */ 211 nmap = 1; 212 error = xfs_bmapi_read(mp->m_rsumip, off, 1, &map, &nmap, 213 XFS_DATA_FORK); 214 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, off, &error)) 215 return error; 216 217 if (nmap != 1 || !xfs_bmap_is_written_extent(&map)) { 218 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, off); 219 return 0; 220 } 221 222 /* Read a block's worth of ondisk rtsummary file. */ 223 error = xfs_rtsummary_read_buf(&args, off); 224 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, off, &error)) 225 return error; 226 227 /* Read a block's worth of computed rtsummary file. */ 228 error = xfsum_copyout(sc, sumoff, sc->buf, mp->m_blockwsize); 229 if (error) { 230 xfs_rtbuf_cache_relse(&args); 231 return error; 232 } 233 234 ondisk_info = xfs_rsumblock_infoptr(&args, 0); 235 if (memcmp(ondisk_info, sc->buf, 236 mp->m_blockwsize << XFS_WORDLOG) != 0) 237 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, off); 238 239 xfs_rtbuf_cache_relse(&args); 240 sumoff += mp->m_blockwsize; 241 } 242 243 return 0; 244 } 245 246 /* Scrub the realtime summary. */ 247 int 248 xchk_rtsummary( 249 struct xfs_scrub *sc) 250 { 251 struct xfs_mount *mp = sc->mp; 252 int error = 0; 253 254 /* Invoke the fork scrubber. */ 255 error = xchk_metadata_inode_forks(sc); 256 if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 257 goto out_rbm; 258 259 /* Construct the new summary file from the rtbitmap. */ 260 error = xchk_rtsum_compute(sc); 261 if (error == -EFSCORRUPTED) { 262 /* 263 * EFSCORRUPTED means the rtbitmap is corrupt, which is an xref 264 * error since we're checking the summary file. 265 */ 266 xchk_ino_xref_set_corrupt(sc, mp->m_rbmip->i_ino); 267 error = 0; 268 goto out_rbm; 269 } 270 if (error) 271 goto out_rbm; 272 273 /* Does the computed summary file match the actual rtsummary file? */ 274 error = xchk_rtsum_compare(sc); 275 276 out_rbm: 277 /* Unlock the rtbitmap since we're done with it. */ 278 xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP); 279 return error; 280 } 281