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_obj_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_obj_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_obj_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