FILE
quotacheck_repair.c
File preview with syntax highlighting.
linux-6.19.14/fs/xfs/scrub/quotacheck_repair.c
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. */ 43static int 44xqcheck_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 error = xchk_trans_alloc(xqc->sc, 0); 56 if (error) 57 return error; 58 59 mutex_lock(&dq->q_qlock); 60 xfs_trans_dqjoin(xqc->sc->tp, dq); 61 62 if (xchk_iscan_aborted(&xqc->iscan)) { 63 error = -ECANCELED; 64 goto out_cancel; 65 } 66 67 mutex_lock(&xqc->lock); 68 error = xfarray_load_sparse(counts, dq->q_id, &xcdq); 69 if (error) 70 goto out_unlock; 71 72 /* Adjust counters as needed. */ 73 delta = (int64_t)xcdq.icount - dq->q_ino.count; 74 if (delta) { 75 dq->q_ino.reserved += delta; 76 dq->q_ino.count += delta; 77 dirty = true; 78 } 79 80 delta = (int64_t)xcdq.bcount - dq->q_blk.count; 81 if (delta) { 82 dq->q_blk.reserved += delta; 83 dq->q_blk.count += delta; 84 dirty = true; 85 } 86 87 delta = (int64_t)xcdq.rtbcount - dq->q_rtb.count; 88 if (delta) { 89 dq->q_rtb.reserved += delta; 90 dq->q_rtb.count += delta; 91 dirty = true; 92 } 93 94 xcdq.flags |= (XQCHECK_DQUOT_REPAIR_SCANNED | XQCHECK_DQUOT_WRITTEN); 95 error = xfarray_store(counts, dq->q_id, &xcdq); 96 if (error == -EFBIG) { 97 /* 98 * EFBIG means we tried to store data at too high a byte offset 99 * in the sparse array. IOWs, we cannot complete the repair 100 * and must cancel the whole operation. This should never 101 * happen, but we need to catch it anyway. 102 */ 103 error = -ECANCELED; 104 } 105 mutex_unlock(&xqc->lock); 106 if (error || !dirty) 107 goto out_cancel; 108 109 trace_xrep_quotacheck_dquot(xqc->sc->mp, dq->q_type, dq->q_id); 110 111 /* Commit the dirty dquot to disk. */ 112 dq->q_flags |= XFS_DQFLAG_DIRTY; 113 if (dq->q_id) 114 xfs_qm_adjust_dqtimers(dq); 115 xfs_trans_log_dquot(xqc->sc->tp, dq); 116 return xrep_trans_commit(xqc->sc); 117 118out_unlock: 119 mutex_unlock(&xqc->lock); 120out_cancel: 121 xchk_trans_cancel(xqc->sc); 122 return error; 123} 124 125/* Commit new quota counters for a particular quota type. */ 126STATIC int 127xqcheck_commit_dqtype( 128 struct xqcheck *xqc, 129 unsigned int dqtype) 130{ 131 struct xchk_dqiter cursor = { }; 132 struct xqcheck_dquot xcdq; 133 struct xfs_scrub *sc = xqc->sc; 134 struct xfs_mount *mp = sc->mp; 135 struct xfarray *counts = xqcheck_counters_for(xqc, dqtype); 136 struct xfs_dquot *dq; 137 xfarray_idx_t cur = XFARRAY_CURSOR_INIT; 138 int error; 139 140 /* 141 * Update the counters of every dquot that the quota file knows about. 142 */ 143 xchk_dqiter_init(&cursor, sc, dqtype); 144 while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) { 145 error = xqcheck_commit_dquot(xqc, dqtype, dq); 146 xfs_qm_dqrele(dq); 147 if (error) 148 break; 149 } 150 if (error) 151 return error; 152 153 /* 154 * Make a second pass to deal with the dquots that we know about but 155 * the quota file previously did not know about. 156 */ 157 mutex_lock(&xqc->lock); 158 while ((error = xfarray_iter(counts, &cur, &xcdq)) == 1) { 159 xfs_dqid_t id = cur - 1; 160 161 if (xcdq.flags & XQCHECK_DQUOT_REPAIR_SCANNED) 162 continue; 163 164 mutex_unlock(&xqc->lock); 165 166 /* 167 * Grab the dquot, allowing for dquot block allocation in a 168 * separate transaction. We committed the scrub transaction 169 * in a previous step, so we will not be creating nested 170 * transactions here. 171 */ 172 error = xfs_qm_dqget(mp, id, dqtype, true, &dq); 173 if (error) 174 return error; 175 176 error = xqcheck_commit_dquot(xqc, dqtype, dq); 177 xfs_qm_dqrele(dq); 178 if (error) 179 return error; 180 181 mutex_lock(&xqc->lock); 182 } 183 mutex_unlock(&xqc->lock); 184 185 return error; 186} 187 188/* Figure out quota CHKD flags for the running quota types. */ 189static inline unsigned int 190xqcheck_chkd_flags( 191 struct xfs_mount *mp) 192{ 193 unsigned int ret = 0; 194 195 if (XFS_IS_UQUOTA_ON(mp)) 196 ret |= XFS_UQUOTA_CHKD; 197 if (XFS_IS_GQUOTA_ON(mp)) 198 ret |= XFS_GQUOTA_CHKD; 199 if (XFS_IS_PQUOTA_ON(mp)) 200 ret |= XFS_PQUOTA_CHKD; 201 return ret; 202} 203 204/* Commit the new dquot counters. */ 205int 206xrep_quotacheck( 207 struct xfs_scrub *sc) 208{ 209 struct xqcheck *xqc = sc->buf; 210 unsigned int qflags = xqcheck_chkd_flags(sc->mp); 211 int error; 212 213 /* 214 * Clear the CHKD flag for the running quota types and commit the scrub 215 * transaction so that we can allocate new quota block mappings if we 216 * have to. If we crash after this point, the sb still has the CHKD 217 * flags cleared, so mount quotacheck will fix all of this up. 218 */ 219 xrep_update_qflags(sc, qflags, 0); 220 error = xrep_trans_commit(sc); 221 if (error) 222 return error; 223 224 /* Commit the new counters to the dquots. */ 225 if (xqc->ucounts) { 226 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_USER); 227 if (error) 228 return error; 229 } 230 if (xqc->gcounts) { 231 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_GROUP); 232 if (error) 233 return error; 234 } 235 if (xqc->pcounts) { 236 error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_PROJ); 237 if (error) 238 return error; 239 } 240 241 /* Set the CHKD flags now that we've fixed quota counts. */ 242 error = xchk_trans_alloc(sc, 0); 243 if (error) 244 return error; 245 246 xrep_update_qflags(sc, 0, qflags); 247 return xrep_trans_commit(sc); 248}
~/
Read-only file preview.