Skip to content

Commit 98bdbf6

Browse files
author
Chandan Babu R
committed
Merge tag 'repair-quota-6.8_2023-12-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.8-mergeB
xfs: online repair of quota and rt metadata files XFS stores quota records and free space bitmap information in files. Add the necessary infrastructure to enable repairing metadata inodes and their forks, and then make it so that we can repair the file metadata for the rtbitmap. Repairing the bitmap contents (and the summary file) is left for subsequent patchsets. We also add the ability to repair file metadata the quota files. As part of these repairs, we also reinitialize the ondisk dquot records as necessary to get the incore dquots working. We can also correct obviously bad dquot record attributes, but we leave checking the resource usage counts for the next patchsets. Signed-off-by: Darrick J. Wong <[email protected]> Signed-off-by: Chandan Babu R <[email protected]> * tag 'repair-quota-6.8_2023-12-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux: xfs: repair quotas xfs: improve dquot iteration for scrub xfs: check dquot resource timers xfs: check the ondisk space mapping behind a dquot
2 parents 5bb4ad9 + a5b9155 commit 98bdbf6

File tree

12 files changed

+1026
-54
lines changed

12 files changed

+1026
-54
lines changed

fs/xfs/Makefile

Lines changed: 8 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)
@@ -196,5 +199,9 @@ xfs-y += $(addprefix scrub/, \
196199
xfs-$(CONFIG_XFS_RT) += $(addprefix scrub/, \
197200
rtbitmap_repair.o \
198201
)
202+
203+
xfs-$(CONFIG_XFS_QUOTA) += $(addprefix scrub/, \
204+
quota_repair.o \
205+
)
199206
endif
200207
endif

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 <[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+
}

0 commit comments

Comments
 (0)