1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2023 Oracle. All Rights Reserved.
8 #include "xfs_shared.h"
10 #include "xfs_format.h"
11 #include "xfs_trans_resv.h"
12 #include "xfs_mount.h"
13 #include "xfs_log_format.h"
14 #include "xfs_trans.h"
15 #include "xfs_inode.h"
16 #include "xfs_quota.h"
19 #include "scrub/scrub.h"
20 #include "scrub/common.h"
21 #include "scrub/quota.h"
22 #include "scrub/trace.h"
24 /* Initialize a dquot iteration cursor. */
27 struct xchk_dqiter *cursor,
32 cursor->bmap.br_startoff = NULLFILEOFF;
33 cursor->dqtype = dqtype & XFS_DQTYPE_REC_MASK;
34 cursor->quota_ip = xfs_quota_inode(sc->mp, cursor->dqtype);
39 * Ensure that the cached data fork mapping for the dqiter cursor is fresh and
40 * covers the dquot pointed to by the scan cursor.
43 xchk_dquot_iter_revalidate_bmap(
44 struct xchk_dqiter *cursor)
46 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
47 struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip,
49 xfs_fileoff_t fileoff;
50 xfs_dqid_t this_id = cursor->id;
54 fileoff = this_id / qi->qi_dqperchunk;
57 * If we have a mapping for cursor->id and it's still fresh, there's
58 * no need to reread the bmbt.
60 if (cursor->bmap.br_startoff != NULLFILEOFF &&
61 cursor->if_seq == ifp->if_seq &&
62 cursor->bmap.br_startoff + cursor->bmap.br_blockcount > fileoff)
65 /* Look up the data fork mapping for the dquot id of interest. */
66 error = xfs_bmapi_read(cursor->quota_ip, fileoff,
67 XFS_MAX_FILEOFF - fileoff, &cursor->bmap, &nmaps, 0);
74 if (cursor->bmap.br_startoff > fileoff) {
75 ASSERT(cursor->bmap.br_startoff == fileoff);
79 cursor->if_seq = ifp->if_seq;
80 trace_xchk_dquot_iter_revalidate_bmap(cursor, cursor->id);
84 /* Advance the dqiter cursor to the next non-sparse region of the quota file. */
86 xchk_dquot_iter_advance_bmap(
87 struct xchk_dqiter *cursor,
88 uint64_t *next_ondisk_id)
90 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
91 struct xfs_ifork *ifp = xfs_ifork_ptr(cursor->quota_ip,
93 xfs_fileoff_t fileoff;
98 /* Find the dquot id for the next non-hole mapping. */
100 fileoff = cursor->bmap.br_startoff + cursor->bmap.br_blockcount;
101 if (fileoff > XFS_DQ_ID_MAX / qi->qi_dqperchunk) {
102 /* The hole goes beyond the max dquot id, we're done */
103 *next_ondisk_id = -1ULL;
107 error = xfs_bmapi_read(cursor->quota_ip, fileoff,
108 XFS_MAX_FILEOFF - fileoff, &cursor->bmap,
113 /* Must have reached the end of the mappings. */
114 *next_ondisk_id = -1ULL;
117 if (cursor->bmap.br_startoff > fileoff) {
118 ASSERT(cursor->bmap.br_startoff == fileoff);
119 return -EFSCORRUPTED;
121 } while (!xfs_bmap_is_real_extent(&cursor->bmap));
123 next_id = cursor->bmap.br_startoff * qi->qi_dqperchunk;
124 if (next_id > XFS_DQ_ID_MAX) {
125 /* The hole goes beyond the max dquot id, we're done */
126 *next_ondisk_id = -1ULL;
130 /* Propose jumping forward to the dquot in the next allocated block. */
131 *next_ondisk_id = next_id;
132 cursor->if_seq = ifp->if_seq;
133 trace_xchk_dquot_iter_advance_bmap(cursor, *next_ondisk_id);
138 * Find the id of the next highest incore dquot. Normally this will correspond
139 * exactly with the quota file block mappings, but repair might have erased a
140 * mapping because it was crosslinked; in that case, we need to re-allocate the
141 * space so that we can reset q_blkno.
144 xchk_dquot_iter_advance_incore(
145 struct xchk_dqiter *cursor,
146 uint64_t *next_incore_id)
148 struct xfs_quotainfo *qi = cursor->sc->mp->m_quotainfo;
149 struct radix_tree_root *tree = xfs_dquot_tree(qi, cursor->dqtype);
150 struct xfs_dquot *dq;
151 unsigned int nr_found;
153 *next_incore_id = -1ULL;
155 mutex_lock(&qi->qi_tree_lock);
156 nr_found = radix_tree_gang_lookup(tree, (void **)&dq, cursor->id, 1);
158 *next_incore_id = dq->q_id;
159 mutex_unlock(&qi->qi_tree_lock);
161 trace_xchk_dquot_iter_advance_incore(cursor, *next_incore_id);
165 * Walk all incore dquots of this filesystem. Caller must set *@cursorp to
166 * zero before the first call, and must not hold the quota file ILOCK.
167 * Returns 1 and a valid *@dqpp; 0 and *@dqpp == NULL when there are no more
168 * dquots to iterate; or a negative errno.
172 struct xchk_dqiter *cursor,
173 struct xfs_dquot **dqpp)
175 struct xfs_mount *mp = cursor->sc->mp;
176 struct xfs_dquot *dq = NULL;
177 uint64_t next_ondisk, next_incore = -1ULL;
178 unsigned int lock_mode;
181 if (cursor->id > XFS_DQ_ID_MAX)
183 next_ondisk = cursor->id;
185 /* Revalidate and/or advance the cursor. */
186 lock_mode = xfs_ilock_data_map_shared(cursor->quota_ip);
187 error = xchk_dquot_iter_revalidate_bmap(cursor);
188 if (!error && !xfs_bmap_is_real_extent(&cursor->bmap))
189 error = xchk_dquot_iter_advance_bmap(cursor, &next_ondisk);
190 xfs_iunlock(cursor->quota_ip, lock_mode);
194 if (next_ondisk > cursor->id)
195 xchk_dquot_iter_advance_incore(cursor, &next_incore);
197 /* Pick the next dquot in the sequence and return it. */
198 cursor->id = min(next_ondisk, next_incore);
199 if (cursor->id > XFS_DQ_ID_MAX)
202 trace_xchk_dquot_iter(cursor, cursor->id);
204 error = xfs_qm_dqget(mp, cursor->id, cursor->dqtype, false, &dq);
208 cursor->id = dq->q_id + 1;