]>
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 <dirent.h> | |
9 | #include <sys/types.h> | |
10 | #include <sys/statvfs.h> | |
11 | #include "list.h" | |
12 | #include "libfrog/paths.h" | |
13 | #include "libfrog/workqueue.h" | |
14 | #include "xfs_scrub.h" | |
15 | #include "common.h" | |
16 | #include "progress.h" | |
17 | #include "scrub.h" | |
18 | #include "repair.h" | |
19 | #include "vfs.h" | |
20 | ||
21 | /* Phase 4: Repair filesystem. */ | |
22 | ||
23 | /* Fix all the problems in our per-AG list. */ | |
24 | static void | |
25 | repair_ag( | |
26 | struct workqueue *wq, | |
27 | xfs_agnumber_t agno, | |
28 | void *priv) | |
29 | { | |
30 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
31 | bool *aborted = priv; | |
32 | struct action_list *alist; | |
33 | size_t unfixed; | |
34 | size_t new_unfixed; | |
35 | unsigned int flags = 0; | |
36 | int ret; | |
37 | ||
38 | alist = &ctx->action_lists[agno]; | |
39 | unfixed = action_list_length(alist); | |
40 | ||
41 | /* Repair anything broken until we fail to make progress. */ | |
42 | do { | |
43 | ret = action_list_process(ctx, ctx->mnt.fd, alist, flags); | |
44 | if (ret) { | |
45 | *aborted = true; | |
46 | return; | |
47 | } | |
48 | new_unfixed = action_list_length(alist); | |
49 | if (new_unfixed == unfixed) | |
50 | break; | |
51 | unfixed = new_unfixed; | |
52 | if (*aborted) | |
53 | return; | |
54 | } while (unfixed > 0); | |
55 | ||
56 | /* Try once more, but this time complain if we can't fix things. */ | |
57 | flags |= ALP_COMPLAIN_IF_UNFIXED; | |
58 | ret = action_list_process(ctx, ctx->mnt.fd, alist, flags); | |
59 | if (ret) | |
60 | *aborted = true; | |
61 | } | |
62 | ||
63 | /* Process all the action items. */ | |
64 | static int | |
65 | repair_everything( | |
66 | struct scrub_ctx *ctx) | |
67 | { | |
68 | struct workqueue wq; | |
69 | xfs_agnumber_t agno; | |
70 | bool aborted = false; | |
71 | int ret; | |
72 | ||
73 | ret = workqueue_create(&wq, (struct xfs_mount *)ctx, | |
74 | scrub_nproc_workqueue(ctx)); | |
75 | if (ret) { | |
76 | str_liberror(ctx, ret, _("creating repair workqueue")); | |
77 | return ret; | |
78 | } | |
79 | for (agno = 0; !aborted && agno < ctx->mnt.fsgeom.agcount; agno++) { | |
80 | if (action_list_length(&ctx->action_lists[agno]) == 0) | |
81 | continue; | |
82 | ||
83 | ret = workqueue_add(&wq, repair_ag, agno, &aborted); | |
84 | if (ret) { | |
85 | str_liberror(ctx, ret, _("queueing repair work")); | |
86 | break; | |
87 | } | |
88 | } | |
89 | ||
90 | ret = workqueue_terminate(&wq); | |
91 | if (ret) | |
92 | str_liberror(ctx, ret, _("finishing repair work")); | |
93 | workqueue_destroy(&wq); | |
94 | ||
95 | if (aborted) | |
96 | return ECANCELED; | |
97 | ||
98 | pthread_mutex_lock(&ctx->lock); | |
99 | if (ctx->corruptions_found == 0 && ctx->unfixable_errors == 0 && | |
100 | want_fstrim) { | |
101 | fstrim(ctx); | |
102 | progress_add(1); | |
103 | } | |
104 | pthread_mutex_unlock(&ctx->lock); | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | /* Fix everything that needs fixing. */ | |
110 | int | |
111 | phase4_func( | |
112 | struct scrub_ctx *ctx) | |
113 | { | |
114 | int ret; | |
115 | ||
116 | /* | |
117 | * Check the summary counters early. Normally we do this during phase | |
118 | * seven, but some of the cross-referencing requires fairly-accurate | |
119 | * counters, so counter repairs have to be put on the list now so that | |
120 | * they get fixed before we stop retrying unfixed metadata repairs. | |
121 | */ | |
122 | ret = xfs_scrub_fs_summary(ctx, &ctx->action_lists[0]); | |
123 | if (ret) | |
124 | return ret; | |
125 | ||
126 | return repair_everything(ctx); | |
127 | } | |
128 | ||
129 | bool | |
130 | xfs_repair_fs( | |
131 | struct scrub_ctx *ctx) | |
132 | { | |
133 | return phase4_func(ctx) == 0; | |
134 | } | |
135 | ||
136 | /* Estimate how much work we're going to do. */ | |
137 | int | |
138 | phase4_estimate( | |
139 | struct scrub_ctx *ctx, | |
140 | uint64_t *items, | |
141 | unsigned int *nr_threads, | |
142 | int *rshift) | |
143 | { | |
144 | xfs_agnumber_t agno; | |
145 | size_t need_fixing = 0; | |
146 | ||
147 | for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) | |
148 | need_fixing += action_list_length(&ctx->action_lists[agno]); | |
149 | need_fixing++; | |
150 | *items = need_fixing; | |
151 | *nr_threads = scrub_nproc(ctx) + 1; | |
152 | *rshift = 0; | |
153 | return 0; | |
154 | } | |
155 | ||
156 | bool | |
157 | xfs_estimate_repair_work( | |
158 | struct scrub_ctx *ctx, | |
159 | uint64_t *items, | |
160 | unsigned int *nr_threads, | |
161 | int *rshift) | |
162 | { | |
163 | return phase4_estimate(ctx, items, nr_threads, rshift) == 0; | |
164 | } |