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_log_format.h" 13 #include "xfs_trans.h" 14 #include "xfs_inode.h" 15 #include "xfs_quota.h" 16 #include "xfs_qm.h" 17 #include "xfs_icache.h" 18 #include "xfs_bmap_util.h" 19 #include "xfs_iwalk.h" 20 #include "xfs_ialloc.h" 21 #include "xfs_sb.h" 22 #include "scrub/scrub.h" 23 #include "scrub/common.h" 24 #include "scrub/repair.h" 25 #include "scrub/xfile.h" 26 #include "scrub/xfarray.h" 27 #include "scrub/iscan.h" 28 #include "scrub/quota.h" 29 #include "scrub/quotacheck.h" 30 #include "scrub/trace.h" 31 32 /* 33 * Live Quotacheck Repair 34 * ====================== 35 * 36 * Use the live quota counter information that we collected to replace the 37 * counter values in the incore dquots. A scrub->repair cycle should have left 38 * the live data and hooks active, so this is safe so long as we make sure the 39 * dquot is locked. 40 */ 41 42 /* Commit new counters to a dquot. */ 43 static int 44 xqcheck_commit_dquot( 45 struct xqcheck *xqc, 46 xfs_dqtype_t dqtype, 47 struct xfs_dquot *dq) 48 { 49 struct xqcheck_dquot xcdq; 50 struct xfarray *counts = xqcheck_counters_for(xqc, dqtype); 51 int64_t delta; 52 bool dirty = false; 53 int error = 0; 54 55 /* Unlock the dquot just long enough to allocate a transaction. */ 56 xfs_dqunlock(dq); 57 error = xchk_trans_alloc(xqc->sc, 0); 58 xfs_dqlock(dq); 59 if (error) 60 return error; 61 62 xfs_trans_dqjoin(xqc->sc->tp, dq); 63 64 if (xchk_iscan_aborted(&xqc->iscan)) { 65 error = -ECANCELED; 66 goto out_cancel; 67 } 68 69 mutex_lock(&xqc->lock); 70 error = xfarray_load_sparse(counts, dq->q_id, &xcdq); 71 if (error) 72 goto out_unlock; 73 74 /* Adjust counters as needed. */ 75 delta = (int64_t)xcdq.icount - dq->q_ino.count; 76 if (delta) { 77 dq->q_ino.reserved += delta; 78 dq->q_ino.count += delta; 79 dirty = true; 80 } 81 82 delta = (int64_t)xcdq.bcount - dq->q_blk.count; 83 if (delta) { 84 dq->q_blk.reserved += delta; 85 dq->q_blk.count += delta; 86 dirty = true; 87 } 88 89 delta = (int64_t)xcdq.rtbcount - dq->q_rtb.count; 90 if (delta) { 91 dq->q_rtb.reserved += delta; 92 dq->q_rtb.count += delta; 93 dirty = true; 94 } 95 96 xcdq.flags |= (XQCHECK_DQUOT_REPAIR_SCANNED | XQCHECK_DQUOT_WRITTEN); 97 error = xfarray_store(counts, dq->q_id, &xcdq); 98 if (error == -EFBIG) { 99 /* 100 * EFBIG means we tried to store data at too high a byte offset 101 * in the sparse array. IOWs, we cannot complete the repair 102 * and must cancel the whole operation. This should never 103 * happen, but we need to catch it anyway. 104 */ 105 error = -ECANCELED; 106 } 107 mutex_unlock(&xqc->lock); 108 if (error || !dirty) 109 goto out_cancel; 110 111 trace_xrep_quotacheck_dquot(xqc->sc->mp, dq->q_type, dq->q_id); 112 113 /* Commit the dirty dquot to disk. */ 114 dq->q_flags |= XFS_DQFLAG_DIRTY; 115 if (dq->q_id) 116 xfs_qm_adjust_dqtimers(dq); 117 xfs_trans_log_dquot(xqc->sc->tp, dq); 118 119 /* 120 * Transaction commit unlocks the dquot, so we must re-lock it so that 121 * the caller can put the reference (which apparently requires a locked 122 * dquot). 123 */ 124 error = xrep_trans_commit(xqc->sc); 125 xfs_dqlock(dq); 126 return error; 127 128 out_unlock: 129 mutex_unlock(&xqc->lock); 130 out_cancel: 131 xchk_trans_cancel(xqc->sc); 132 133 /* Re-lock the dquot so the caller can put the reference. */ 134 xfs_dqlock(dq); 135 return error; 136 } 137 138 /* Commit new quota counters for a particular quota type. */ 139 STATIC int 140 xqcheck_commit_dqtype( 141 struct xqcheck *xqc, 142 unsigned int dqtype) 143 { 144 struct xchk_dqiter cursor = { }; 145 struct xqcheck_dquot xcdq; 146 struct xfs_scrub *sc = xqc->sc; 147 struct xfs_mount *mp = sc->mp; 148 struct xfarray *counts = xqcheck_counters_for(xqc, dqtype); 149 struct xfs_dquot *dq; 150 xfarray_idx_t cur = XFARRAY_CURSOR_INIT; 151 int error; 152 153 /* 154 * Update the counters of every dquot that the quota file knows about. 155 */ 156 xchk_dqiter_init(&cursor, sc, dqtype); 157 while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) { 158 error = xqcheck_commit_dquot(xqc, dqtype, dq); 159 xfs_qm_dqput(dq); 160 if (error) 161 break; 162 } 163 if (error) 164 return error; 165 166 /* 167 * Make a second pass to deal with the dquots that we know about but 168 * the quota file previously did not know about. 169 */ 170 mutex_lock(&xqc->lock); 171 while ((error = xfarray_iter(counts, &cur, &xcdq)) == 1) { 172 xfs_dqid_t id = cur - 1; 173 174 if (xcdq.flags & XQCHECK_DQUOT_REPAIR_SCANNED) 175 continue; 176 177 mutex_unlock(&xqc->lock); 178 179 /* 180 * Grab the dquot, allowing for dquot block allocation in a 181 * separate transaction. We committed the scrub transaction 182 * in a previous step, so we will not be creating nested 183 * transactions here. 184 */ 185 error = xfs_qm_dqget(mp, id, dqtype, true, &dq); 186 if (error) 187 return error; 188 189 error = xqcheck_commit_dquot(xqc, dqtype, dq); 190 xfs_qm_dqput(dq); 191 if (error) 192 return error; 193 194 mutex_lock(&xqc->lock); 195 } 196 mutex_unlock(&xqc->lock); 197 198 return error; 199 } 200 201 /* Figure out quota CHKD flags for the running quota types. */ 202 static inline unsigned int 203 xqcheck_chkd_flags( 204 struct xfs_mount *mp) 205 { 206 unsigned int ret = 0; 207 208 if (XFS_IS_UQUOTA_ON(mp)) 209 ret |= XFS_UQUOTA_CHKD; 210 if (XFS_IS_GQUOTA_ON(mp)) 211 ret |= XFS_GQUOTA_CHKD; 212 if (XFS_IS_PQUOTA_ON(mp)) 213 ret |= XFS_PQUOTA_CHKD; 214 return ret; 215 } 216 217 /* Commit the new dquot counters. */ 218 int 219 xrep_quotacheck( 220 struct xfs_scrub *sc) 221 { 222 struct xqcheck *xqc = sc->buf; 223 unsigned int qflags = xqcheck_chkd_flags(sc->mp); 224 int error; 225 226 /* 227 * Clear the CHKD flag for the running quota types and commit the scrub 228 * transaction so that we can allocate new quota block mappings if we 229 * have to. If we crash after this point, the sb still has the CHKD 230 * flags cleared, so mount quotacheck will fix all of this up. 231 */ 232 xrep_update_qflags(sc, qflags, 0); 233 error = xrep_trans_commit(sc); 234 if (error) 235 return error; 236 237 /* Commit the new counters to the dquots. */ 238 if (xqc->ucounts) { 239 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_USER); 240 if (error) 241 return error; 242 } 243 if (xqc->gcounts) { 244 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_GROUP); 245 if (error) 246 return error; 247 } 248 if (xqc->pcounts) { 249 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_PROJ); 250 if (error) 251 return error; 252 } 253 254 /* Set the CHKD flags now that we've fixed quota counts. */ 255 error = xchk_trans_alloc(sc, 0); 256 if (error) 257 return error; 258 259 xrep_update_qflags(sc, 0, qflags); 260 return xrep_trans_commit(sc); 261 } 262
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.