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 = XFS_B_TO_FSB(mp, mp->m_rsumsize); 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 xfs_filblks_t rsumblocks; 104 int error; 105 106 /* We require the rmapbt to rebuild anything. */ 107 if (!xfs_has_rmapbt(mp)) 108 return -EOPNOTSUPP; 109 /* We require atomic file exchange range to rebuild anything. */ 110 if (!xfs_has_exchange_range(mp)) 111 return -EOPNOTSUPP; 112 113 /* Walk away if we disagree on the size of the rt bitmap. */ 114 if (rts->rbmblocks != mp->m_sb.sb_rbmblocks) 115 return 0; 116 117 /* Make sure any problems with the fork are fixed. */ 118 error = xrep_metadata_inode_forks(sc); 119 if (error) 120 return error; 121 122 /* 123 * Try to take ILOCK_EXCL of the temporary file. We had better be the 124 * only ones holding onto this inode, but we can't block while holding 125 * the rtsummary file's ILOCK_EXCL. 126 */ 127 while (!xrep_tempfile_ilock_nowait(sc)) { 128 if (xchk_should_terminate(sc, &error)) 129 return error; 130 delay(1); 131 } 132 133 /* Make sure we have space allocated for the entire summary file. */ 134 rsumblocks = XFS_B_TO_FSB(mp, rts->rsumsize); 135 xfs_trans_ijoin(sc->tp, sc->ip, 0); 136 xfs_trans_ijoin(sc->tp, sc->tempip, 0); 137 error = xrep_tempfile_prealloc(sc, 0, rsumblocks); 138 if (error) 139 return error; 140 141 /* Last chance to abort before we start committing fixes. */ 142 if (xchk_should_terminate(sc, &error)) 143 return error; 144 145 /* Copy the rtsummary file that we generated. */ 146 error = xrep_tempfile_copyin(sc, 0, rsumblocks, 147 xrep_rtsummary_prep_buf, rts); 148 if (error) 149 return error; 150 error = xrep_tempfile_set_isize(sc, rts->rsumsize); 151 if (error) 152 return error; 153 154 /* 155 * Now exchange the contents. Nothing in repair uses the temporary 156 * buffer, so we can reuse it for the tempfile exchrange information. 157 */ 158 error = xrep_tempexch_trans_reserve(sc, XFS_DATA_FORK, &rts->tempexch); 159 if (error) 160 return error; 161 162 error = xrep_tempexch_contents(sc, &rts->tempexch); 163 if (error) 164 return error; 165 166 /* Reset incore state and blow out the summary cache. */ 167 if (mp->m_rsum_cache) 168 memset(mp->m_rsum_cache, 0xFF, mp->m_sb.sb_rbmblocks); 169 170 mp->m_rsumlevels = rts->rsumlevels; 171 mp->m_rsumsize = rts->rsumsize; 172 173 /* Free the old rtsummary blocks if they're not in use. */ 174 return xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK); 175 } 176