Skip to content

Commit 21d7500

Browse files
author
Darrick J. Wong
committed
xfs: improve dquot iteration for scrub
Upon a closer inspection of the quota record scrubber, I noticed that dqiterate wasn't actually walking all possible dquots for the mapped blocks in the quota file. This is due to xfs_qm_dqget_next skipping all XFS_IS_DQUOT_UNINITIALIZED dquots. For a fsck program, we really want to look at all the dquots, even if all counters and limits in the dquot record are zero. Rewrite the implementation to do this, as well as switching to an iterator paradigm to reduce the number of indirect calls. This enables removal of the old broken dqiterate code from xfs_dquot.c. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
1 parent 774b5c0 commit 21d7500

File tree

9 files changed

+319
-46
lines changed

9 files changed

+319
-46
lines changed

fs/xfs/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,10 @@ xfs-$(CONFIG_XFS_RT) += $(addprefix scrub/, \
176176
rtsummary.o \
177177
)
178178

179-
xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o
179+
xfs-$(CONFIG_XFS_QUOTA) += $(addprefix scrub/, \
180+
dqiterate.o \
181+
quota.o \
182+
)
180183

181184
# online repair
182185
ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y)

fs/xfs/libxfs/xfs_format.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,9 @@ static inline time64_t xfs_dq_bigtime_to_unix(uint32_t ondisk_seconds)
12721272
#define XFS_DQ_GRACE_MIN ((int64_t)0)
12731273
#define XFS_DQ_GRACE_MAX ((int64_t)U32_MAX)
12741274

1275+
/* Maximum id value for a quota record */
1276+
#define XFS_DQ_ID_MAX (U32_MAX)
1277+
12751278
/*
12761279
* This is the main portion of the on-disk representation of quota information
12771280
* for a user. We pad this with some more expansion room to construct the on

fs/xfs/scrub/dqiterate.c

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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+
}

fs/xfs/scrub/quota.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "xfs_bmap.h"
1919
#include "scrub/scrub.h"
2020
#include "scrub/common.h"
21+
#include "scrub/quota.h"
2122

2223
/* Convert a scrub type code to a DQ flag, or return 0 if error. */
2324
static inline xfs_dqtype_t
@@ -137,11 +138,9 @@ xchk_quota_item_timer(
137138
/* Scrub the fields in an individual quota item. */
138139
STATIC int
139140
xchk_quota_item(
140-
struct xfs_dquot *dq,
141-
xfs_dqtype_t dqtype,
142-
void *priv)
141+
struct xchk_quota_info *sqi,
142+
struct xfs_dquot *dq)
143143
{
144-
struct xchk_quota_info *sqi = priv;
145144
struct xfs_scrub *sc = sqi->sc;
146145
struct xfs_mount *mp = sc->mp;
147146
struct xfs_quotainfo *qi = mp->m_quotainfo;
@@ -270,7 +269,7 @@ xchk_quota_data_fork(
270269
return error;
271270

272271
/* Check for data fork problems that apply only to quota files. */
273-
max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
272+
max_dqid_off = XFS_DQ_ID_MAX / qi->qi_dqperchunk;
274273
ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
275274
for_each_xfs_iext(ifp, &icur, &irec) {
276275
if (xchk_should_terminate(sc, &error))
@@ -297,9 +296,11 @@ int
297296
xchk_quota(
298297
struct xfs_scrub *sc)
299298
{
300-
struct xchk_quota_info sqi;
299+
struct xchk_dqiter cursor = { };
300+
struct xchk_quota_info sqi = { .sc = sc };
301301
struct xfs_mount *mp = sc->mp;
302302
struct xfs_quotainfo *qi = mp->m_quotainfo;
303+
struct xfs_dquot *dq;
303304
xfs_dqtype_t dqtype;
304305
int error = 0;
305306

@@ -318,9 +319,15 @@ xchk_quota(
318319
* functions.
319320
*/
320321
xchk_iunlock(sc, sc->ilock_flags);
321-
sqi.sc = sc;
322-
sqi.last_id = 0;
323-
error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi);
322+
323+
/* Now look for things that the quota verifiers won't complain about. */
324+
xchk_dqiter_init(&cursor, sc, dqtype);
325+
while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) {
326+
error = xchk_quota_item(&sqi, dq);
327+
xfs_qm_dqput(dq);
328+
if (error)
329+
break;
330+
}
324331
xchk_ilock(sc, XFS_ILOCK_EXCL);
325332
if (error == -ECANCELED)
326333
error = 0;

fs/xfs/scrub/quota.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2018-2023 Oracle. All Rights Reserved.
4+
* Author: Darrick J. Wong <djwong@kernel.org>
5+
*/
6+
#ifndef __XFS_SCRUB_QUOTA_H__
7+
#define __XFS_SCRUB_QUOTA_H__
8+
9+
/* dquot iteration code */
10+
11+
struct xchk_dqiter {
12+
struct xfs_scrub *sc;
13+
14+
/* Quota file that we're walking. */
15+
struct xfs_inode *quota_ip;
16+
17+
/* Cached data fork mapping for the dquot. */
18+
struct xfs_bmbt_irec bmap;
19+
20+
/* The next dquot to scan. */
21+
uint64_t id;
22+
23+
/* Quota type (user/group/project). */
24+
xfs_dqtype_t dqtype;
25+
26+
/* Data fork sequence number to detect stale mappings. */
27+
unsigned int if_seq;
28+
};
29+
30+
void xchk_dqiter_init(struct xchk_dqiter *cursor, struct xfs_scrub *sc,
31+
xfs_dqtype_t dqtype);
32+
int xchk_dquot_iter(struct xchk_dqiter *cursor, struct xfs_dquot **dqpp);
33+
34+
#endif /* __XFS_SCRUB_QUOTA_H__ */

fs/xfs/scrub/trace.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
#include "xfs_btree.h"
1515
#include "xfs_ag.h"
1616
#include "xfs_rtbitmap.h"
17+
#include "xfs_quota.h"
1718
#include "scrub/scrub.h"
1819
#include "scrub/xfile.h"
1920
#include "scrub/xfarray.h"
21+
#include "scrub/quota.h"
2022

2123
/* Figure out which block the btree cursor was pointing to. */
2224
static inline xfs_fsblock_t

0 commit comments

Comments
 (0)