File Index
Files and directories
Sign in
FILE

quotacheck_repair.c

File preview with syntax highlighting.

linux-6.19.14/fs/xfs/scrub/quotacheck_repair.c 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.