]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
e758ad01 DW |
2 | /* |
3 | * Copyright (C) 2018 Oracle. All Rights Reserved. | |
e758ad01 | 4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> |
e758ad01 | 5 | */ |
a440f877 | 6 | #include "xfs.h" |
e758ad01 | 7 | #include <stdint.h> |
e758ad01 | 8 | #include <sys/types.h> |
e758ad01 | 9 | #include <sys/statvfs.h> |
19852474 | 10 | #include "list.h" |
42b4c8e8 | 11 | #include "libfrog/paths.h" |
56598728 | 12 | #include "libfrog/workqueue.h" |
e758ad01 DW |
13 | #include "xfs_scrub.h" |
14 | #include "common.h" | |
15 | #include "scrub.h" | |
ee310b0c | 16 | #include "repair.h" |
e758ad01 DW |
17 | |
18 | /* Phase 2: Check internal metadata. */ | |
19 | ||
20 | /* Scrub each AG's metadata btrees. */ | |
21 | static void | |
f29dc2f5 | 22 | scan_ag_metadata( |
e758ad01 DW |
23 | struct workqueue *wq, |
24 | xfs_agnumber_t agno, | |
25 | void *arg) | |
26 | { | |
27 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
f29dc2f5 | 28 | bool *aborted = arg; |
83d2c80b DW |
29 | struct action_list alist; |
30 | struct action_list immediate_alist; | |
ee310b0c DW |
31 | unsigned long long broken_primaries; |
32 | unsigned long long broken_secondaries; | |
e758ad01 | 33 | char descr[DESCR_BUFSZ]; |
d22f2471 | 34 | int ret; |
e758ad01 | 35 | |
f29dc2f5 DW |
36 | if (*aborted) |
37 | return; | |
38 | ||
83d2c80b DW |
39 | action_list_init(&alist); |
40 | action_list_init(&immediate_alist); | |
e758ad01 DW |
41 | snprintf(descr, DESCR_BUFSZ, _("AG %u"), agno); |
42 | ||
43 | /* | |
44 | * First we scrub and fix the AG headers, because we need | |
45 | * them to work well enough to check the AG btrees. | |
46 | */ | |
d22f2471 DW |
47 | ret = xfs_scrub_ag_headers(ctx, agno, &alist); |
48 | if (ret) | |
ee310b0c DW |
49 | goto err; |
50 | ||
51 | /* Repair header damage. */ | |
83d2c80b DW |
52 | ret = action_list_process_or_defer(ctx, agno, &alist); |
53 | if (ret) | |
e758ad01 DW |
54 | goto err; |
55 | ||
56 | /* Now scrub the AG btrees. */ | |
d22f2471 DW |
57 | ret = xfs_scrub_ag_metadata(ctx, agno, &alist); |
58 | if (ret) | |
ee310b0c DW |
59 | goto err; |
60 | ||
61 | /* | |
62 | * Figure out if we need to perform early fixing. The only | |
63 | * reason we need to do this is if the inobt is broken, which | |
64 | * prevents phase 3 (inode scan) from running. We can rebuild | |
65 | * the inobt from rmapbt data, but if the rmapbt is broken even | |
66 | * at this early phase then we are sunk. | |
67 | */ | |
68 | broken_secondaries = 0; | |
69 | broken_primaries = 0; | |
83d2c80b | 70 | action_list_find_mustfix(&alist, &immediate_alist, |
ee310b0c DW |
71 | &broken_primaries, &broken_secondaries); |
72 | if (broken_secondaries && !debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) { | |
73 | if (broken_primaries) | |
74 | str_info(ctx, descr, | |
75 | _("Corrupt primary and secondary block mapping metadata.")); | |
76 | else | |
77 | str_info(ctx, descr, | |
78 | _("Corrupt secondary block mapping metadata.")); | |
79 | str_info(ctx, descr, | |
80 | _("Filesystem might not be repairable.")); | |
81 | } | |
82 | ||
83 | /* Repair (inode) btree damage. */ | |
83d2c80b DW |
84 | ret = action_list_process_or_defer(ctx, agno, &immediate_alist); |
85 | if (ret) | |
e758ad01 DW |
86 | goto err; |
87 | ||
ee310b0c | 88 | /* Everything else gets fixed during phase 4. */ |
83d2c80b | 89 | action_list_defer(ctx, agno, &alist); |
e758ad01 DW |
90 | return; |
91 | err: | |
f29dc2f5 | 92 | *aborted = true; |
e758ad01 DW |
93 | } |
94 | ||
95 | /* Scrub whole-FS metadata btrees. */ | |
96 | static void | |
f29dc2f5 | 97 | scan_fs_metadata( |
e758ad01 DW |
98 | struct workqueue *wq, |
99 | xfs_agnumber_t agno, | |
100 | void *arg) | |
101 | { | |
102 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
f29dc2f5 | 103 | bool *aborted = arg; |
83d2c80b | 104 | struct action_list alist; |
d22f2471 | 105 | int ret; |
e758ad01 | 106 | |
f29dc2f5 DW |
107 | if (*aborted) |
108 | return; | |
109 | ||
83d2c80b | 110 | action_list_init(&alist); |
d22f2471 | 111 | ret = xfs_scrub_fs_metadata(ctx, &alist); |
f29dc2f5 DW |
112 | if (ret) { |
113 | *aborted = true; | |
114 | return; | |
115 | } | |
ee310b0c | 116 | |
83d2c80b | 117 | action_list_defer(ctx, agno, &alist); |
e758ad01 DW |
118 | } |
119 | ||
120 | /* Scan all filesystem metadata. */ | |
f29dc2f5 DW |
121 | int |
122 | phase2_func( | |
e758ad01 DW |
123 | struct scrub_ctx *ctx) |
124 | { | |
83d2c80b | 125 | struct action_list alist; |
e758ad01 DW |
126 | struct workqueue wq; |
127 | xfs_agnumber_t agno; | |
f29dc2f5 DW |
128 | bool aborted = false; |
129 | int ret, ret2; | |
e758ad01 DW |
130 | |
131 | ret = workqueue_create(&wq, (struct xfs_mount *)ctx, | |
132 | scrub_nproc_workqueue(ctx)); | |
133 | if (ret) { | |
9d57cbfc | 134 | str_liberror(ctx, ret, _("creating scrub workqueue")); |
f29dc2f5 | 135 | return ret; |
e758ad01 DW |
136 | } |
137 | ||
138 | /* | |
139 | * In case we ever use the primary super scrubber to perform fs | |
140 | * upgrades (followed by a full scrub), do that before we launch | |
141 | * anything else. | |
142 | */ | |
83d2c80b | 143 | action_list_init(&alist); |
d22f2471 | 144 | ret = xfs_scrub_primary_super(ctx, &alist); |
f29dc2f5 | 145 | if (ret) |
ee310b0c | 146 | goto out; |
83d2c80b | 147 | ret = action_list_process_or_defer(ctx, 0, &alist); |
f29dc2f5 | 148 | if (ret) |
224df902 | 149 | goto out; |
e758ad01 | 150 | |
f29dc2f5 DW |
151 | for (agno = 0; !aborted && agno < ctx->mnt.fsgeom.agcount; agno++) { |
152 | ret = workqueue_add(&wq, scan_ag_metadata, agno, &aborted); | |
e758ad01 | 153 | if (ret) { |
9d57cbfc | 154 | str_liberror(ctx, ret, _("queueing per-AG scrub work")); |
e758ad01 DW |
155 | goto out; |
156 | } | |
157 | } | |
158 | ||
f29dc2f5 | 159 | if (aborted) |
e758ad01 DW |
160 | goto out; |
161 | ||
f29dc2f5 | 162 | ret = workqueue_add(&wq, scan_fs_metadata, 0, &aborted); |
e758ad01 | 163 | if (ret) { |
9d57cbfc | 164 | str_liberror(ctx, ret, _("queueing per-FS scrub work")); |
e758ad01 DW |
165 | goto out; |
166 | } | |
167 | ||
168 | out: | |
f29dc2f5 DW |
169 | ret2 = workqueue_terminate(&wq); |
170 | if (ret2) { | |
171 | str_liberror(ctx, ret2, _("finishing scrub work")); | |
172 | if (!ret && ret2) | |
173 | ret = ret2; | |
71296cf8 | 174 | } |
e758ad01 | 175 | workqueue_destroy(&wq); |
f29dc2f5 DW |
176 | |
177 | if (!ret && aborted) | |
178 | ret = ECANCELED; | |
179 | return ret; | |
e758ad01 | 180 | } |
ed60d210 | 181 | |
f29dc2f5 DW |
182 | /* Estimate how much work we're going to do. */ |
183 | int | |
184 | phase2_estimate( | |
ed60d210 DW |
185 | struct scrub_ctx *ctx, |
186 | uint64_t *items, | |
187 | unsigned int *nr_threads, | |
188 | int *rshift) | |
189 | { | |
d22f2471 | 190 | *items = scrub_estimate_ag_work(ctx); |
ed60d210 DW |
191 | *nr_threads = scrub_nproc(ctx); |
192 | *rshift = 0; | |
f29dc2f5 DW |
193 | return 0; |
194 | } |