1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2020-2024 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_log_format.h" 14 #include "xfs_trans.h" 15 #include "xfs_rtalloc.h" 16 #include "xfs_inode.h" 17 #include "xfs_bit.h" 18 #include "xfs_bmap.h" 19 #include "xfs_bmap_btree.h" 20 #include "xfs_exchmaps.h" 21 #include "xfs_rtbitmap.h" 22 #include "scrub/scrub.h" 23 #include "scrub/common.h" 24 #include "scrub/trace.h" 25 #include "scrub/repair.h" 26 #include "scrub/tempfile.h" 27 #include "scrub/tempexch.h" 28 #include "scrub/reap.h" 29 #include "scrub/xfile.h" 30 #include "scrub/rtsummary.h" 31 32 /* Set us up to repair the rtsummary file. */ 33 int 34 xrep_setup_rtsummary( 35 struct xfs_scrub *sc, 36 struct xchk_rtsummary *rts) 37 { 38 struct xfs_mount *mp = sc->mp; 39 unsigned long long blocks; 40 int error; 41 42 error = xrep_tempfile_create(sc, S_IFREG); 43 if (error) 44 return error; 45 46 /* 47 * If we're doing a repair, we reserve enough blocks to write out a 48 * completely new summary file, plus twice as many blocks as we would 49 * need if we can only allocate one block per data fork mapping. This 50 * should cover the preallocation of the temporary file and exchanging 51 * the extent mappings. 52 * 53 * We cannot use xfs_exchmaps_estimate because we have not yet 54 * constructed the replacement rtsummary and therefore do not know how 55 * many extents it will use. By the time we do, we will have a dirty 56 * transaction (which we cannot drop because we cannot drop the 57 * rtsummary ILOCK) and cannot ask for more reservation. 58 */ 59 blocks = mp->m_rsumblocks; 60 blocks += xfs_bmbt_calc_size(mp, blocks) * 2; 61 if (blocks > UINT_MAX) 62 return -EOPNOTSUPP; 63 64 rts->resblks += blocks; 65 return 0; 66 } 67 68 static int 69 xrep_rtsummary_prep_buf( 70 struct xfs_scrub *sc, 71 struct xfs_buf *bp, 72 void *data) 73 { 74 struct xchk_rtsummary *rts = data; 75 struct xfs_mount *mp = sc->mp; 76 union xfs_suminfo_raw *ondisk; 77 int error; 78 79 rts->args.mp = sc->mp; 80 rts->args.tp = sc->tp; 81 rts->args.sumbp = bp; 82 ondisk = xfs_rsumblock_infoptr(&rts->args, 0); 83 rts->args.sumbp = NULL; 84 85 bp->b_ops = &xfs_rtbuf_ops; 86 87 error = xfsum_copyout(sc, rts->prep_wordoff, ondisk, mp->m_blockwsize); 88 if (error) 89 return error; 90 91 rts->prep_wordoff += mp->m_blockwsize; 92 xfs_trans_buf_set_type(sc->tp, bp, XFS_BLFT_RTSUMMARY_BUF); 93 return 0; 94 } 95 96 /* Repair the realtime summary. */ 97 int 98 xrep_rtsummary( 99 struct xfs_scrub *sc) 100 { 101 struct xchk_rtsummary *rts = sc->buf; 102 struct xfs_mount *mp = sc->mp; 103 int error; 104 105 /* We require the rmapbt to rebuild anything. */ 106 if (!xfs_has_rmapbt(mp)) 107 return -EOPNOTSUPP; 108 /* We require atomic file exchange range to rebuild anything. */ 109 if (!xfs_has_exchange_range(mp)) 110 return -EOPNOTSUPP; 111 112 /* Walk away if we disagree on the size of the rt bitmap. */ 113 if (rts->rbmblocks != mp->m_sb.sb_rbmblocks) 114 return 0; 115 116 /* Make sure any problems with the fork are fixed. */ 117 error = xrep_metadata_inode_forks(sc); 118 if (error) 119 return error; 120 121 /* 122 * Try to take ILOCK_EXCL of the temporary file. We had better be the 123 * only ones holding onto this inode, but we can't block while holding 124 * the rtsummary file's ILOCK_EXCL. 125 */ 126 while (!xrep_tempfile_ilock_nowait(sc)) { 127 if (xchk_should_terminate(sc, &error)) 128 return error; 129 delay(1); 130 } 131 132 /* Make sure we have space allocated for the entire summary file. */ 133 xfs_trans_ijoin(sc->tp, sc->ip, 0); 134 xfs_trans_ijoin(sc->tp, sc->tempip, 0); 135 error = xrep_tempfile_prealloc(sc, 0, rts->rsumblocks); 136 if (error) 137 return error; 138 139 /* Last chance to abort before we start committing fixes. */ 140 if (xchk_should_terminate(sc, &error)) 141 return error; 142 143 /* Copy the rtsummary file that we generated. */ 144 error = xrep_tempfile_copyin(sc, 0, rts->rsumblocks, 145 xrep_rtsummary_prep_buf, rts); 146 if (error) 147 return error; 148 error = xrep_tempfile_set_isize(sc, XFS_FSB_TO_B(mp, rts->rsumblocks)); 149 if (error) 150 return error; 151 152 /* 153 * Now exchange the contents. Nothing in repair uses the temporary 154 * buffer, so we can reuse it for the tempfile exchrange information. 155 */ 156 error = xrep_tempexch_trans_reserve(sc, XFS_DATA_FORK, &rts->tempexch); 157 if (error) 158 return error; 159 160 error = xrep_tempexch_contents(sc, &rts->tempexch); 161 if (error) 162 return error; 163 164 /* Reset incore state and blow out the summary cache. */ 165 if (mp->m_rsum_cache) 166 memset(mp->m_rsum_cache, 0xFF, mp->m_sb.sb_rbmblocks); 167 168 mp->m_rsumlevels = rts->rsumlevels; 169 mp->m_rsumblocks = rts->rsumblocks; 170 171 /* Free the old rtsummary blocks if they're not in use. */ 172 return xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK); 173 } 174