]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/repair.c
xfs_scrub: remove moveon from repair action list helpers
[thirdparty/xfsprogs-dev.git] / scrub / repair.c
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 /* fall through */
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 (xfs_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 }