262 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 Repair34 * ======================35 *36 * Use the live quota counter information that we collected to replace the37 * counter values in the incore dquots. A scrub->repair cycle should have left38 * the live data and hooks active, so this is safe so long as we make sure the39 * dquot is locked.40 */41 42/* Commit new counters to a dquot. */43static int44xqcheck_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 offset101 * in the sparse array. IOWs, we cannot complete the repair102 * and must cancel the whole operation. This should never103 * 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 that121 * the caller can put the reference (which apparently requires a locked122 * dquot).123 */124 error = xrep_trans_commit(xqc->sc);125 xfs_dqlock(dq);126 return error;127 128out_unlock:129 mutex_unlock(&xqc->lock);130out_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. */139STATIC int140xqcheck_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 but168 * 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 a181 * separate transaction. We committed the scrub transaction182 * in a previous step, so we will not be creating nested183 * 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. */202static inline unsigned int203xqcheck_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. */218int219xrep_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 scrub228 * transaction so that we can allocate new quota block mappings if we229 * have to. If we crash after this point, the sb still has the CHKD230 * 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