]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0+ | |
2 | /* | |
3 | * Copyright (C) 2018 Oracle. All Rights Reserved. | |
4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> | |
5 | */ | |
6 | #include "xfs.h" | |
7 | #include <stdint.h> | |
8 | #include <stdlib.h> | |
9 | #include <sys/types.h> | |
10 | #include <sys/statvfs.h> | |
11 | #include "list.h" | |
12 | #include "libfrog/paths.h" | |
13 | #include "xfs_scrub.h" | |
14 | #include "common.h" | |
15 | #include "scrub.h" | |
16 | #include "progress.h" | |
17 | #include "repair.h" | |
18 | ||
19 | /* | |
20 | * Prioritize action items in order of how long we can wait. | |
21 | * 0 = do it now, 10000 = do it later. | |
22 | * | |
23 | * To minimize the amount of repair work, we want to prioritize metadata | |
24 | * objects by perceived corruptness. If CORRUPT is set, the fields are | |
25 | * just plain bad; try fixing that first. Otherwise if XCORRUPT is set, | |
26 | * the fields could be bad, but the xref data could also be bad; we'll | |
27 | * try fixing that next. Finally, if XFAIL is set, some other metadata | |
28 | * structure failed validation during xref, so we'll recheck this | |
29 | * metadata last since it was probably fine. | |
30 | * | |
31 | * For metadata that lie in the critical path of checking other metadata | |
32 | * (superblock, AG{F,I,FL}, inobt) we scrub and fix those things before | |
33 | * we even get to handling their dependencies, so things should progress | |
34 | * in order. | |
35 | */ | |
36 | ||
37 | /* Sort action items in severity order. */ | |
38 | static int | |
39 | PRIO( | |
40 | struct action_item *aitem, | |
41 | int order) | |
42 | { | |
43 | if (aitem->flags & XFS_SCRUB_OFLAG_CORRUPT) | |
44 | return order; | |
45 | else if (aitem->flags & XFS_SCRUB_OFLAG_XCORRUPT) | |
46 | return 100 + order; | |
47 | else if (aitem->flags & XFS_SCRUB_OFLAG_XFAIL) | |
48 | return 200 + order; | |
49 | else if (aitem->flags & XFS_SCRUB_OFLAG_PREEN) | |
50 | return 300 + order; | |
51 | abort(); | |
52 | } | |
53 | ||
54 | /* Sort the repair items in dependency order. */ | |
55 | static int | |
56 | xfs_action_item_priority( | |
57 | struct action_item *aitem) | |
58 | { | |
59 | switch (aitem->type) { | |
60 | case XFS_SCRUB_TYPE_SB: | |
61 | case XFS_SCRUB_TYPE_AGF: | |
62 | case XFS_SCRUB_TYPE_AGFL: | |
63 | case XFS_SCRUB_TYPE_AGI: | |
64 | case XFS_SCRUB_TYPE_BNOBT: | |
65 | case XFS_SCRUB_TYPE_CNTBT: | |
66 | case XFS_SCRUB_TYPE_INOBT: | |
67 | case XFS_SCRUB_TYPE_FINOBT: | |
68 | case XFS_SCRUB_TYPE_REFCNTBT: | |
69 | case XFS_SCRUB_TYPE_RMAPBT: | |
70 | case XFS_SCRUB_TYPE_INODE: | |
71 | case XFS_SCRUB_TYPE_BMBTD: | |
72 | case XFS_SCRUB_TYPE_BMBTA: | |
73 | case XFS_SCRUB_TYPE_BMBTC: | |
74 | return PRIO(aitem, aitem->type - 1); | |
75 | case XFS_SCRUB_TYPE_DIR: | |
76 | case XFS_SCRUB_TYPE_XATTR: | |
77 | case XFS_SCRUB_TYPE_SYMLINK: | |
78 | case XFS_SCRUB_TYPE_PARENT: | |
79 | return PRIO(aitem, XFS_SCRUB_TYPE_DIR); | |
80 | case XFS_SCRUB_TYPE_RTBITMAP: | |
81 | case XFS_SCRUB_TYPE_RTSUM: | |
82 | return PRIO(aitem, XFS_SCRUB_TYPE_RTBITMAP); | |
83 | case XFS_SCRUB_TYPE_UQUOTA: | |
84 | case XFS_SCRUB_TYPE_GQUOTA: | |
85 | case XFS_SCRUB_TYPE_PQUOTA: | |
86 | return PRIO(aitem, XFS_SCRUB_TYPE_UQUOTA); | |
87 | case XFS_SCRUB_TYPE_FSCOUNTERS: | |
88 | /* This should always go after AG headers no matter what. */ | |
89 | return PRIO(aitem, INT_MAX); | |
90 | } | |
91 | abort(); | |
92 | } | |
93 | ||
94 | /* Make sure that btrees get repaired before headers. */ | |
95 | static int | |
96 | xfs_action_item_compare( | |
97 | void *priv, | |
98 | struct list_head *a, | |
99 | struct list_head *b) | |
100 | { | |
101 | struct action_item *ra; | |
102 | struct action_item *rb; | |
103 | ||
104 | ra = container_of(a, struct action_item, list); | |
105 | rb = container_of(b, struct action_item, list); | |
106 | ||
107 | return xfs_action_item_priority(ra) - xfs_action_item_priority(rb); | |
108 | } | |
109 | ||
110 | /* | |
111 | * Figure out which AG metadata must be fixed before we can move on | |
112 | * to the inode scan. | |
113 | */ | |
114 | void | |
115 | action_list_find_mustfix( | |
116 | struct action_list *alist, | |
117 | struct action_list *immediate_alist, | |
118 | unsigned long long *broken_primaries, | |
119 | unsigned long long *broken_secondaries) | |
120 | { | |
121 | struct action_item *n; | |
122 | struct action_item *aitem; | |
123 | ||
124 | list_for_each_entry_safe(aitem, n, &alist->list, list) { | |
125 | if (!(aitem->flags & XFS_SCRUB_OFLAG_CORRUPT)) | |
126 | continue; | |
127 | switch (aitem->type) { | |
128 | case XFS_SCRUB_TYPE_RMAPBT: | |
129 | (*broken_secondaries)++; | |
130 | break; | |
131 | case XFS_SCRUB_TYPE_FINOBT: | |
132 | case XFS_SCRUB_TYPE_INOBT: | |
133 | alist->nr--; | |
134 | list_move_tail(&aitem->list, &immediate_alist->list); | |
135 | immediate_alist->nr++; | |
136 | fallthrough; | |
137 | case XFS_SCRUB_TYPE_BNOBT: | |
138 | case XFS_SCRUB_TYPE_CNTBT: | |
139 | case XFS_SCRUB_TYPE_REFCNTBT: | |
140 | (*broken_primaries)++; | |
141 | break; | |
142 | default: | |
143 | abort(); | |
144 | break; | |
145 | } | |
146 | } | |
147 | } | |
148 | ||
149 | /* | |
150 | * Allocate a certain number of repair lists for the scrub context. Returns | |
151 | * zero or a positive error number. | |
152 | */ | |
153 | int | |
154 | action_lists_alloc( | |
155 | size_t nr, | |
156 | struct action_list **listsp) | |
157 | { | |
158 | struct action_list *lists; | |
159 | xfs_agnumber_t agno; | |
160 | ||
161 | lists = calloc(nr, sizeof(struct action_list)); | |
162 | if (!lists) | |
163 | return errno; | |
164 | ||
165 | for (agno = 0; agno < nr; agno++) | |
166 | action_list_init(&lists[agno]); | |
167 | *listsp = lists; | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | /* Free the repair lists. */ | |
173 | void | |
174 | action_lists_free( | |
175 | struct action_list **listsp) | |
176 | { | |
177 | free(*listsp); | |
178 | *listsp = NULL; | |
179 | } | |
180 | ||
181 | /* Initialize repair list */ | |
182 | void | |
183 | action_list_init( | |
184 | struct action_list *alist) | |
185 | { | |
186 | INIT_LIST_HEAD(&alist->list); | |
187 | alist->nr = 0; | |
188 | alist->sorted = false; | |
189 | } | |
190 | ||
191 | /* Number of repairs in this list. */ | |
192 | size_t | |
193 | action_list_length( | |
194 | struct action_list *alist) | |
195 | { | |
196 | return alist->nr; | |
197 | }; | |
198 | ||
199 | /* Add to the list of repairs. */ | |
200 | void | |
201 | action_list_add( | |
202 | struct action_list *alist, | |
203 | struct action_item *aitem) | |
204 | { | |
205 | list_add_tail(&aitem->list, &alist->list); | |
206 | alist->nr++; | |
207 | alist->sorted = false; | |
208 | } | |
209 | ||
210 | /* Splice two repair lists. */ | |
211 | void | |
212 | action_list_splice( | |
213 | struct action_list *dest, | |
214 | struct action_list *src) | |
215 | { | |
216 | if (src->nr == 0) | |
217 | return; | |
218 | ||
219 | list_splice_tail_init(&src->list, &dest->list); | |
220 | dest->nr += src->nr; | |
221 | src->nr = 0; | |
222 | dest->sorted = false; | |
223 | } | |
224 | ||
225 | /* Repair everything on this list. */ | |
226 | int | |
227 | action_list_process( | |
228 | struct scrub_ctx *ctx, | |
229 | int fd, | |
230 | struct action_list *alist, | |
231 | unsigned int repair_flags) | |
232 | { | |
233 | struct action_item *aitem; | |
234 | struct action_item *n; | |
235 | enum check_outcome fix; | |
236 | ||
237 | if (!alist->sorted) { | |
238 | list_sort(NULL, &alist->list, xfs_action_item_compare); | |
239 | alist->sorted = true; | |
240 | } | |
241 | ||
242 | list_for_each_entry_safe(aitem, n, &alist->list, list) { | |
243 | fix = xfs_repair_metadata(ctx, fd, aitem, repair_flags); | |
244 | switch (fix) { | |
245 | case CHECK_DONE: | |
246 | if (!(repair_flags & ALP_NOPROGRESS)) | |
247 | progress_add(1); | |
248 | alist->nr--; | |
249 | list_del(&aitem->list); | |
250 | free(aitem); | |
251 | continue; | |
252 | case CHECK_ABORT: | |
253 | return ECANCELED; | |
254 | case CHECK_RETRY: | |
255 | continue; | |
256 | case CHECK_REPAIR: | |
257 | abort(); | |
258 | } | |
259 | } | |
260 | ||
261 | if (scrub_excessive_errors(ctx)) | |
262 | return ECANCELED; | |
263 | return 0; | |
264 | } | |
265 | ||
266 | /* Defer all the repairs until phase 4. */ | |
267 | void | |
268 | action_list_defer( | |
269 | struct scrub_ctx *ctx, | |
270 | xfs_agnumber_t agno, | |
271 | struct action_list *alist) | |
272 | { | |
273 | ASSERT(agno < ctx->mnt.fsgeom.agcount); | |
274 | ||
275 | action_list_splice(&ctx->action_lists[agno], alist); | |
276 | } | |
277 | ||
278 | /* Run actions now and defer unfinished items for later. */ | |
279 | int | |
280 | action_list_process_or_defer( | |
281 | struct scrub_ctx *ctx, | |
282 | xfs_agnumber_t agno, | |
283 | struct action_list *alist) | |
284 | { | |
285 | int ret; | |
286 | ||
287 | ret = action_list_process(ctx, ctx->mnt.fd, alist, | |
288 | ALP_REPAIR_ONLY | ALP_NOPROGRESS); | |
289 | if (ret) | |
290 | return ret; | |
291 | ||
292 | action_list_defer(ctx, agno, alist); | |
293 | return 0; | |
294 | } |