xref: /linux/fs/xfs/scrub/dqiterate.c (revision 4b132aacb0768ac1e652cf517097ea6f237214b9)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 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_bit.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"
17 #include "xfs_qm.h"
18 #include "xfs_bmap.h"
19 #include "scrub/scrub.h"
20 #include "scrub/common.h"
21 #include "scrub/quota.h"
22 #include "scrub/trace.h"
23 
24 /* Initialize a dquot iteration cursor. */
25 void
26 xchk_dqiter_init(
27 	struct xchk_dqiter	*cursor,
28 	struct xfs_scrub	*sc,
29 	xfs_dqtype_t		dqtype)
30 {
31 	cursor->sc = sc;
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);
35 	cursor->id = 0;
36 }
37 
38 /*
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.
41  */
42 STATIC int
43 xchk_dquot_iter_revalidate_bmap(
44 	struct xchk_dqiter	*cursor)
45 {
46 	struct xfs_quotainfo	*qi = cursor->sc->mp->m_quotainfo;
47 	struct xfs_ifork	*ifp = xfs_ifork_ptr(cursor->quota_ip,
48 								XFS_DATA_FORK);
49 	xfs_fileoff_t		fileoff;
50 	xfs_dqid_t		this_id = cursor->id;
51 	int			nmaps = 1;
52 	int			error;
53 
54 	fileoff = this_id / qi->qi_dqperchunk;
55 
56 	/*
57 	 * If we have a mapping for cursor->id and it's still fresh, there's
58 	 * no need to reread the bmbt.
59 	 */
60 	if (cursor->bmap.br_startoff != NULLFILEOFF &&
61 	    cursor->if_seq == ifp->if_seq &&
62 	    cursor->bmap.br_startoff + cursor->bmap.br_blockcount > fileoff)
63 		return 0;
64 
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);
68 	if (error)
69 		return error;
70 	if (!nmaps) {
71 		ASSERT(nmaps > 0);
72 		return -EFSCORRUPTED;
73 	}
74 	if (cursor->bmap.br_startoff > fileoff) {
75 		ASSERT(cursor->bmap.br_startoff == fileoff);
76 		return -EFSCORRUPTED;
77 	}
78 
79 	cursor->if_seq = ifp->if_seq;
80 	trace_xchk_dquot_iter_revalidate_bmap(cursor, cursor->id);
81 	return 0;
82 }
83 
84 /* Advance the dqiter cursor to the next non-sparse region of the quota file. */
85 STATIC int
86 xchk_dquot_iter_advance_bmap(
87 	struct xchk_dqiter	*cursor,
88 	uint64_t		*next_ondisk_id)
89 {
90 	struct xfs_quotainfo	*qi = cursor->sc->mp->m_quotainfo;
91 	struct xfs_ifork	*ifp = xfs_ifork_ptr(cursor->quota_ip,
92 								XFS_DATA_FORK);
93 	xfs_fileoff_t		fileoff;
94 	uint64_t		next_id;
95 	int			nmaps = 1;
96 	int			error;
97 
98 	/* Find the dquot id for the next non-hole mapping. */
99 	do {
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;
104 			return 0;
105 		}
106 
107 		error = xfs_bmapi_read(cursor->quota_ip, fileoff,
108 				XFS_MAX_FILEOFF - fileoff, &cursor->bmap,
109 				&nmaps, 0);
110 		if (error)
111 			return error;
112 		if (!nmaps) {
113 			/* Must have reached the end of the mappings. */
114 			*next_ondisk_id = -1ULL;
115 			return 0;
116 		}
117 		if (cursor->bmap.br_startoff > fileoff) {
118 			ASSERT(cursor->bmap.br_startoff == fileoff);
119 			return -EFSCORRUPTED;
120 		}
121 	} while (!xfs_bmap_is_real_extent(&cursor->bmap));
122 
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;
127 		return 0;
128 	}
129 
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);
134 	return 0;
135 }
136 
137 /*
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.
142  */
143 STATIC void
144 xchk_dquot_iter_advance_incore(
145 	struct xchk_dqiter	*cursor,
146 	uint64_t		*next_incore_id)
147 {
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;
152 
153 	*next_incore_id = -1ULL;
154 
155 	mutex_lock(&qi->qi_tree_lock);
156 	nr_found = radix_tree_gang_lookup(tree, (void **)&dq, cursor->id, 1);
157 	if (nr_found)
158 		*next_incore_id = dq->q_id;
159 	mutex_unlock(&qi->qi_tree_lock);
160 
161 	trace_xchk_dquot_iter_advance_incore(cursor, *next_incore_id);
162 }
163 
164 /*
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.
169  */
170 int
171 xchk_dquot_iter(
172 	struct xchk_dqiter	*cursor,
173 	struct xfs_dquot	**dqpp)
174 {
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;
179 	int			error = 0;
180 
181 	if (cursor->id > XFS_DQ_ID_MAX)
182 		return 0;
183 	next_ondisk = cursor->id;
184 
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);
191 	if (error)
192 		return error;
193 
194 	if (next_ondisk > cursor->id)
195 		xchk_dquot_iter_advance_incore(cursor, &next_incore);
196 
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)
200 		return 0;
201 
202 	trace_xchk_dquot_iter(cursor, cursor->id);
203 
204 	error = xfs_qm_dqget(mp, cursor->id, cursor->dqtype, false, &dq);
205 	if (error)
206 		return error;
207 
208 	cursor->id = dq->q_id + 1;
209 	*dqpp = dq;
210 	return 1;
211 }
212