]> Git Repo - linux.git/blob - fs/xfs/scrub/dqiterate.c
x86/config: Fix warning for 'make ARCH=x86_64 tinyconfig'
[linux.git] / fs / xfs / scrub / dqiterate.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2023 Oracle.  All Rights Reserved.
4  * Author: Darrick J. Wong <[email protected]>
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 }
This page took 0.045244 seconds and 4 git commands to generate.