]>
Commit | Line | Data |
---|---|---|
8d318d62 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ee310b0c | 2 | /* |
52520522 | 3 | * Copyright (C) 2018-2024 Oracle. All Rights Reserved. |
8d318d62 | 4 | * Author: Darrick J. Wong <djwong@kernel.org> |
ee310b0c DW |
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" | |
42b4c8e8 | 12 | #include "libfrog/paths.h" |
ee310b0c DW |
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( | |
c6b593ee | 40 | const struct action_item *aitem, |
ee310b0c DW |
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( | |
c6b593ee | 57 | const struct action_item *aitem) |
ee310b0c DW |
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); | |
cbaf1c9d DW |
87 | case XFS_SCRUB_TYPE_FSCOUNTERS: |
88 | /* This should always go after AG headers no matter what. */ | |
89 | return PRIO(aitem, INT_MAX); | |
ee310b0c DW |
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, | |
c6b593ee DW |
98 | const struct list_head *a, |
99 | const struct list_head *b) | |
ee310b0c | 100 | { |
c6b593ee DW |
101 | const struct action_item *ra; |
102 | const struct action_item *rb; | |
ee310b0c DW |
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 | |
83d2c80b DW |
115 | action_list_find_mustfix( |
116 | struct action_list *alist, | |
117 | struct action_list *immediate_alist, | |
ee310b0c DW |
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++; | |
7c432f77 | 136 | fallthrough; |
ee310b0c DW |
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 | ||
83d2c80b DW |
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( | |
ee310b0c | 155 | size_t nr, |
83d2c80b | 156 | struct action_list **listsp) |
ee310b0c | 157 | { |
83d2c80b | 158 | struct action_list *lists; |
ee310b0c DW |
159 | xfs_agnumber_t agno; |
160 | ||
83d2c80b | 161 | lists = calloc(nr, sizeof(struct action_list)); |
ee310b0c | 162 | if (!lists) |
83d2c80b | 163 | return errno; |
ee310b0c DW |
164 | |
165 | for (agno = 0; agno < nr; agno++) | |
83d2c80b | 166 | action_list_init(&lists[agno]); |
ee310b0c DW |
167 | *listsp = lists; |
168 | ||
83d2c80b | 169 | return 0; |
ee310b0c DW |
170 | } |
171 | ||
172 | /* Free the repair lists. */ | |
173 | void | |
83d2c80b DW |
174 | action_lists_free( |
175 | struct action_list **listsp) | |
ee310b0c DW |
176 | { |
177 | free(*listsp); | |
178 | *listsp = NULL; | |
179 | } | |
180 | ||
181 | /* Initialize repair list */ | |
182 | void | |
83d2c80b DW |
183 | action_list_init( |
184 | struct action_list *alist) | |
ee310b0c DW |
185 | { |
186 | INIT_LIST_HEAD(&alist->list); | |
187 | alist->nr = 0; | |
188 | alist->sorted = false; | |
189 | } | |
190 | ||
191 | /* Number of repairs in this list. */ | |
0e5dce33 | 192 | unsigned long long |
83d2c80b DW |
193 | action_list_length( |
194 | struct action_list *alist) | |
ee310b0c DW |
195 | { |
196 | return alist->nr; | |
197 | }; | |
198 | ||
199 | /* Add to the list of repairs. */ | |
200 | void | |
83d2c80b DW |
201 | action_list_add( |
202 | struct action_list *alist, | |
ee310b0c DW |
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 | |
83d2c80b DW |
212 | action_list_splice( |
213 | struct action_list *dest, | |
214 | struct action_list *src) | |
ee310b0c DW |
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. */ | |
83d2c80b DW |
226 | int |
227 | action_list_process( | |
ee310b0c DW |
228 | struct scrub_ctx *ctx, |
229 | int fd, | |
83d2c80b | 230 | struct action_list *alist, |
ee310b0c DW |
231 | unsigned int repair_flags) |
232 | { | |
bb9be147 DW |
233 | struct xfs_fd xfd; |
234 | struct xfs_fd *xfdp = &ctx->mnt; | |
ee310b0c DW |
235 | struct action_item *aitem; |
236 | struct action_item *n; | |
237 | enum check_outcome fix; | |
238 | ||
bb9be147 DW |
239 | /* |
240 | * If the caller passed us a file descriptor for a scrub, use it | |
241 | * instead of scrub-by-handle because this enables the kernel to skip | |
242 | * costly inode btree lookups. | |
243 | */ | |
244 | if (fd >= 0) { | |
245 | memcpy(&xfd, xfdp, sizeof(xfd)); | |
246 | xfd.fd = fd; | |
247 | xfdp = &xfd; | |
248 | } | |
249 | ||
ee310b0c DW |
250 | if (!alist->sorted) { |
251 | list_sort(NULL, &alist->list, xfs_action_item_compare); | |
252 | alist->sorted = true; | |
253 | } | |
254 | ||
255 | list_for_each_entry_safe(aitem, n, &alist->list, list) { | |
bb9be147 | 256 | fix = xfs_repair_metadata(ctx, xfdp, aitem, repair_flags); |
ee310b0c DW |
257 | switch (fix) { |
258 | case CHECK_DONE: | |
259 | if (!(repair_flags & ALP_NOPROGRESS)) | |
260 | progress_add(1); | |
261 | alist->nr--; | |
262 | list_del(&aitem->list); | |
263 | free(aitem); | |
264 | continue; | |
265 | case CHECK_ABORT: | |
83d2c80b | 266 | return ECANCELED; |
ee310b0c DW |
267 | case CHECK_RETRY: |
268 | continue; | |
269 | case CHECK_REPAIR: | |
270 | abort(); | |
271 | } | |
272 | } | |
273 | ||
273165cc | 274 | if (scrub_excessive_errors(ctx)) |
83d2c80b DW |
275 | return ECANCELED; |
276 | return 0; | |
ee310b0c DW |
277 | } |
278 | ||
279 | /* Defer all the repairs until phase 4. */ | |
280 | void | |
83d2c80b | 281 | action_list_defer( |
ee310b0c DW |
282 | struct scrub_ctx *ctx, |
283 | xfs_agnumber_t agno, | |
83d2c80b | 284 | struct action_list *alist) |
ee310b0c | 285 | { |
3f9efb2e | 286 | ASSERT(agno < ctx->mnt.fsgeom.agcount); |
ee310b0c | 287 | |
83d2c80b | 288 | action_list_splice(&ctx->action_lists[agno], alist); |
ee310b0c DW |
289 | } |
290 | ||
291 | /* Run actions now and defer unfinished items for later. */ | |
83d2c80b DW |
292 | int |
293 | action_list_process_or_defer( | |
ee310b0c DW |
294 | struct scrub_ctx *ctx, |
295 | xfs_agnumber_t agno, | |
83d2c80b | 296 | struct action_list *alist) |
ee310b0c | 297 | { |
83d2c80b | 298 | int ret; |
ee310b0c | 299 | |
bb9be147 | 300 | ret = action_list_process(ctx, -1, alist, |
ee310b0c | 301 | ALP_REPAIR_ONLY | ALP_NOPROGRESS); |
83d2c80b DW |
302 | if (ret) |
303 | return ret; | |
ee310b0c | 304 | |
83d2c80b DW |
305 | action_list_defer(ctx, agno, alist); |
306 | return 0; | |
ee310b0c | 307 | } |