Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /*
3 : * Copyright (C) 2020-2023 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/quotacheck.h"
29 : #include "scrub/trace.h"
30 :
31 : /*
32 : * Live Quotacheck Repair
33 : * ======================
34 : *
35 : * Use the live quota counter information that we collected to replace the
36 : * counter values in the incore dquots. A scrub->repair cycle should have left
37 : * the live data and hooks active, so this is safe so long as we make sure the
38 : * dquot is locked.
39 : */
40 :
41 : /* Commit new counters to a dquot. */
42 : static int
43 1572259 : xqcheck_commit_dquot(
44 : struct xfs_dquot *dqp,
45 : xfs_dqtype_t dqtype,
46 : void *priv)
47 : {
48 1572259 : struct xqcheck_dquot xcdq;
49 1572259 : struct xqcheck *xqc = priv;
50 1572259 : struct xfarray *counts = xqcheck_counters_for(xqc, dqtype);
51 1572259 : int64_t delta;
52 1572259 : bool dirty = false;
53 1572259 : int error = 0;
54 :
55 : /* Unlock the dquot just long enough to allocate a transaction. */
56 1572259 : xfs_dqunlock(dqp);
57 1572259 : error = xchk_trans_alloc(xqc->sc, 0);
58 1572259 : xfs_dqlock(dqp);
59 1572259 : if (error)
60 : return error;
61 :
62 1572259 : xfs_trans_dqjoin(xqc->sc->tp, dqp);
63 :
64 1572259 : if (xchk_iscan_aborted(&xqc->iscan)) {
65 0 : error = -ECANCELED;
66 0 : goto out_cancel;
67 : }
68 :
69 1572259 : mutex_lock(&xqc->lock);
70 1572259 : error = xfarray_load_sparse(counts, dqp->q_id, &xcdq);
71 1572259 : if (error)
72 0 : goto out_unlock;
73 :
74 : /* Adjust counters as needed. */
75 1572259 : delta = (int64_t)xcdq.icount - dqp->q_ino.count;
76 1572259 : if (delta) {
77 0 : dqp->q_ino.reserved += delta;
78 0 : dqp->q_ino.count += delta;
79 0 : dirty = true;
80 : }
81 :
82 1572259 : delta = (int64_t)xcdq.bcount - dqp->q_blk.count;
83 1572259 : if (delta) {
84 0 : dqp->q_blk.reserved += delta;
85 0 : dqp->q_blk.count += delta;
86 0 : dirty = true;
87 : }
88 :
89 1572259 : delta = (int64_t)xcdq.rtbcount - dqp->q_rtb.count;
90 1572259 : if (delta) {
91 0 : dqp->q_rtb.reserved += delta;
92 0 : dqp->q_rtb.count += delta;
93 0 : dirty = true;
94 : }
95 :
96 1572259 : xcdq.flags |= (XQCHECK_DQUOT_REPAIR_SCANNED | XQCHECK_DQUOT_WRITTEN);
97 1572259 : error = xfarray_store(counts, dqp->q_id, &xcdq);
98 1572259 : 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 0 : error = -ECANCELED;
106 : }
107 1572259 : mutex_unlock(&xqc->lock);
108 1572259 : if (error || !dirty)
109 1572259 : goto out_cancel;
110 :
111 0 : trace_xrep_quotacheck_dquot(xqc->sc->mp, dqp->q_type, dqp->q_id);
112 :
113 : /* Commit the dirty dquot to disk. */
114 0 : dqp->q_flags |= XFS_DQFLAG_DIRTY;
115 0 : if (dqp->q_id)
116 0 : xfs_qm_adjust_dqtimers(dqp);
117 0 : xfs_trans_log_dquot(xqc->sc->tp, dqp);
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 0 : error = xrep_trans_commit(xqc->sc);
125 0 : xfs_dqlock(dqp);
126 0 : return error;
127 :
128 : out_unlock:
129 0 : mutex_unlock(&xqc->lock);
130 1572259 : out_cancel:
131 1572259 : xchk_trans_cancel(xqc->sc);
132 :
133 : /* Re-lock the dquot so the caller can put the reference. */
134 1572259 : xfs_dqlock(dqp);
135 1572259 : return error;
136 : }
137 :
138 : /* Commit new quota counters for a particular quota type. */
139 : STATIC int
140 3126 : xqcheck_commit_dqtype(
141 : struct xqcheck *xqc,
142 : unsigned int dqtype)
143 : {
144 3126 : struct xqcheck_dquot xcdq;
145 3126 : struct xfs_scrub *sc = xqc->sc;
146 3126 : struct xfs_mount *mp = sc->mp;
147 3126 : struct xfarray *counts = xqcheck_counters_for(xqc, dqtype);
148 3126 : struct xfs_dquot *dqp;
149 3126 : xfarray_idx_t cur = XFARRAY_CURSOR_INIT;
150 3126 : int error;
151 :
152 : /*
153 : * Update the counters of every dquot that the quota file knows about.
154 : */
155 3126 : error = xfs_qm_dqiterate(mp, dqtype, xqcheck_commit_dquot, xqc);
156 3126 : if (error)
157 : return error;
158 :
159 : /*
160 : * Make a second pass to deal with the dquots that we know about but
161 : * the quota file previously did not know about.
162 : */
163 3126 : mutex_lock(&xqc->lock);
164 1575385 : while ((error = xfarray_iter(counts, &cur, &xcdq)) == 1) {
165 1572259 : xfs_dqid_t id = cur - 1;
166 :
167 1572259 : if (xcdq.flags & XQCHECK_DQUOT_REPAIR_SCANNED)
168 3126 : continue;
169 :
170 1569133 : mutex_unlock(&xqc->lock);
171 :
172 : /*
173 : * Grab the dquot, allowing for dquot block allocation in a
174 : * separate transaction. We committed the scrub transaction
175 : * in a previous step, so we will not be creating nested
176 : * transactions here.
177 : */
178 1569133 : error = xfs_qm_dqget(mp, id, dqtype, true, &dqp);
179 1569133 : if (error)
180 0 : return error;
181 :
182 1569133 : error = xqcheck_commit_dquot(dqp, dqtype, xqc);
183 1569133 : xfs_qm_dqput(dqp);
184 1569133 : if (error)
185 0 : return error;
186 :
187 1569133 : mutex_lock(&xqc->lock);
188 : }
189 3126 : mutex_unlock(&xqc->lock);
190 :
191 3126 : return error;
192 : }
193 :
194 : /* Figure out quota CHKD flags for the running quota types. */
195 : static inline unsigned int
196 1042 : xqcheck_chkd_flags(
197 : struct xfs_mount *mp)
198 : {
199 1042 : unsigned int ret = 0;
200 :
201 1042 : if (XFS_IS_UQUOTA_ON(mp))
202 1042 : ret |= XFS_UQUOTA_CHKD;
203 1042 : if (XFS_IS_GQUOTA_ON(mp))
204 1042 : ret |= XFS_GQUOTA_CHKD;
205 1042 : if (XFS_IS_PQUOTA_ON(mp))
206 1042 : ret |= XFS_PQUOTA_CHKD;
207 1042 : return ret;
208 : }
209 :
210 : /* Commit the new dquot counters. */
211 : int
212 1042 : xrep_quotacheck(
213 : struct xfs_scrub *sc)
214 : {
215 1042 : struct xqcheck *xqc = sc->buf;
216 1042 : unsigned int qflags = xqcheck_chkd_flags(sc->mp);
217 1042 : int error;
218 :
219 : /*
220 : * Clear the CHKD flag for the running quota types and commit the scrub
221 : * transaction so that we can allocate new quota block mappings if we
222 : * have to. If we crash after this point, the sb still has the CHKD
223 : * flags cleared, so mount quotacheck will fix all of this up.
224 : */
225 1042 : xrep_update_qflags(sc, qflags, 0);
226 1042 : error = xrep_trans_commit(sc);
227 1042 : if (error)
228 : return error;
229 :
230 : /* Commit the new counters to the dquots. */
231 1042 : if (xqc->ucounts) {
232 1042 : error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_USER);
233 1042 : if (error)
234 : return error;
235 : }
236 1042 : if (xqc->gcounts) {
237 1042 : error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_GROUP);
238 1042 : if (error)
239 : return error;
240 : }
241 1042 : if (xqc->pcounts) {
242 1042 : error = xqcheck_commit_dqtype(xqc, XFS_DQTYPE_PROJ);
243 1042 : if (error)
244 : return error;
245 : }
246 :
247 : /* Set the CHKD flags now that we've fixed quota counts. */
248 1042 : error = xchk_trans_alloc(sc, 0);
249 1042 : if (error)
250 : return error;
251 :
252 1042 : xrep_update_qflags(sc, 0, qflags);
253 1042 : return 0;
254 : }
|