]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
372d4ba9 DW |
2 | /* |
3 | * Copyright (C) 2018 Oracle. All Rights Reserved. | |
372d4ba9 | 4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> |
372d4ba9 | 5 | */ |
a440f877 | 6 | #include "xfs.h" |
372d4ba9 DW |
7 | #include <stdint.h> |
8 | #include <stdlib.h> | |
9 | #include <pthread.h> | |
10 | #include <sys/statvfs.h> | |
11 | #include "platform_defs.h" | |
372d4ba9 DW |
12 | #include "xfs_arch.h" |
13 | #include "xfs_format.h" | |
14 | #include "handle.h" | |
15 | #include "path.h" | |
56598728 | 16 | #include "libfrog/workqueue.h" |
372d4ba9 DW |
17 | #include "xfs_scrub.h" |
18 | #include "common.h" | |
19 | #include "inodes.h" | |
fee68490 | 20 | #include "libfrog/fsgeom.h" |
f31b5e12 | 21 | #include "libfrog/bulkstat.h" |
372d4ba9 DW |
22 | |
23 | /* | |
24 | * Iterate a range of inodes. | |
25 | * | |
26 | * This is a little more involved than repeatedly asking BULKSTAT for a | |
27 | * buffer's worth of stat data for some number of inodes. We want to scan as | |
28 | * many of the inodes that the inobt thinks there are, including the ones that | |
29 | * are broken, but if we ask for n inodes starting at x, it'll skip the bad | |
30 | * ones and fill from beyond the range (x + n). | |
31 | * | |
32 | * Therefore, we ask INUMBERS to return one inobt chunk's worth of inode | |
33 | * bitmap information. Then we try to BULKSTAT only the inodes that were | |
34 | * present in that chunk, and compare what we got against what INUMBERS said | |
35 | * was there. If there's a mismatch, we know that we have an inode that fails | |
36 | * the verifiers but we can inject the bulkstat information to force the scrub | |
37 | * code to deal with the broken inodes. | |
38 | * | |
39 | * If the iteration function returns ESTALE, that means that the inode has | |
40 | * been deleted and possibly recreated since the BULKSTAT call. We wil | |
41 | * refresh the stat information and try again up to 30 times before reporting | |
42 | * the staleness as an error. | |
43 | */ | |
44 | ||
45 | /* | |
46 | * Did we get exactly the inodes we expected? If not, load them one at a | |
47 | * time (or fake it) into the bulkstat data. | |
48 | */ | |
49 | static void | |
50 | xfs_iterate_inodes_range_check( | |
51 | struct scrub_ctx *ctx, | |
52 | struct xfs_inogrp *inogrp, | |
53 | struct xfs_bstat *bstat) | |
54 | { | |
372d4ba9 | 55 | struct xfs_bstat *bs; |
372d4ba9 DW |
56 | int i; |
57 | int error; | |
58 | ||
372d4ba9 DW |
59 | for (i = 0, bs = bstat; i < XFS_INODES_PER_CHUNK; i++) { |
60 | if (!(inogrp->xi_allocmask & (1ULL << i))) | |
61 | continue; | |
62 | if (bs->bs_ino == inogrp->xi_startino + i) { | |
63 | bs++; | |
64 | continue; | |
65 | } | |
66 | ||
67 | /* Load the one inode. */ | |
f31b5e12 DW |
68 | error = xfrog_bulkstat_single(&ctx->mnt, |
69 | inogrp->xi_startino + i, bs); | |
372d4ba9 DW |
70 | if (error || bs->bs_ino != inogrp->xi_startino + i) { |
71 | memset(bs, 0, sizeof(struct xfs_bstat)); | |
72 | bs->bs_ino = inogrp->xi_startino + i; | |
73 | bs->bs_blksize = ctx->mnt_sv.f_frsize; | |
74 | } | |
75 | bs++; | |
76 | } | |
77 | } | |
78 | ||
79 | /* | |
80 | * Call into the filesystem for inode/bulkstat information and call our | |
81 | * iterator function. We'll try to fill the bulkstat information in batches, | |
82 | * but we also can detect iget failures. | |
83 | */ | |
84 | static bool | |
85 | xfs_iterate_inodes_range( | |
86 | struct scrub_ctx *ctx, | |
87 | const char *descr, | |
88 | void *fshandle, | |
89 | uint64_t first_ino, | |
90 | uint64_t last_ino, | |
91 | xfs_inode_iter_fn fn, | |
92 | void *arg) | |
93 | { | |
372d4ba9 DW |
94 | struct xfs_handle handle; |
95 | struct xfs_inogrp inogrp; | |
96 | struct xfs_bstat bstat[XFS_INODES_PER_CHUNK]; | |
97 | char idescr[DESCR_BUFSZ]; | |
372d4ba9 | 98 | struct xfs_bstat *bs; |
621f3374 | 99 | uint64_t igrp_ino; |
f31b5e12 DW |
100 | uint64_t ino; |
101 | uint32_t bulklen = 0; | |
621f3374 | 102 | uint32_t igrplen = 0; |
372d4ba9 DW |
103 | bool moveon = true; |
104 | int i; | |
105 | int error; | |
106 | int stale_count = 0; | |
107 | ||
108 | ||
109 | memset(bstat, 0, XFS_INODES_PER_CHUNK * sizeof(struct xfs_bstat)); | |
372d4ba9 | 110 | |
372d4ba9 DW |
111 | memcpy(&handle.ha_fsid, fshandle, sizeof(handle.ha_fsid)); |
112 | handle.ha_fid.fid_len = sizeof(xfs_fid_t) - | |
113 | sizeof(handle.ha_fid.fid_len); | |
114 | handle.ha_fid.fid_pad = 0; | |
115 | ||
116 | /* Find the inode chunk & alloc mask */ | |
117 | igrp_ino = first_ino; | |
621f3374 | 118 | error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, &igrplen); |
372d4ba9 DW |
119 | while (!error && igrplen) { |
120 | /* Load the inodes. */ | |
121 | ino = inogrp.xi_startino - 1; | |
f31b5e12 | 122 | |
300661d3 DW |
123 | /* |
124 | * We can have totally empty inode chunks on filesystems where | |
125 | * there are more than 64 inodes per block. Skip these. | |
126 | */ | |
127 | if (inogrp.xi_alloccount == 0) | |
128 | goto igrp_retry; | |
f31b5e12 DW |
129 | error = xfrog_bulkstat(&ctx->mnt, &ino, inogrp.xi_alloccount, |
130 | bstat, &bulklen); | |
131 | if (error) { | |
132 | char errbuf[DESCR_BUFSZ]; | |
133 | ||
134 | str_info(ctx, descr, "%s", strerror_r(error, | |
135 | errbuf, DESCR_BUFSZ)); | |
136 | } | |
372d4ba9 DW |
137 | |
138 | xfs_iterate_inodes_range_check(ctx, &inogrp, bstat); | |
139 | ||
140 | /* Iterate all the inodes. */ | |
141 | for (i = 0, bs = bstat; i < inogrp.xi_alloccount; i++, bs++) { | |
142 | if (bs->bs_ino > last_ino) | |
143 | goto out; | |
144 | ||
145 | handle.ha_fid.fid_ino = bs->bs_ino; | |
146 | handle.ha_fid.fid_gen = bs->bs_gen; | |
147 | error = fn(ctx, &handle, bs, arg); | |
148 | switch (error) { | |
149 | case 0: | |
150 | break; | |
151 | case ESTALE: | |
152 | stale_count++; | |
153 | if (stale_count < 30) { | |
154 | igrp_ino = inogrp.xi_startino; | |
155 | goto igrp_retry; | |
156 | } | |
157 | snprintf(idescr, DESCR_BUFSZ, "inode %"PRIu64, | |
158 | (uint64_t)bs->bs_ino); | |
bb5dbd06 DW |
159 | str_info(ctx, idescr, |
160 | _("Changed too many times during scan; giving up.")); | |
372d4ba9 DW |
161 | break; |
162 | case XFS_ITERATE_INODES_ABORT: | |
163 | error = 0; | |
164 | /* fall thru */ | |
165 | default: | |
166 | moveon = false; | |
167 | errno = error; | |
168 | goto err; | |
169 | } | |
170 | if (xfs_scrub_excessive_errors(ctx)) { | |
171 | moveon = false; | |
172 | goto out; | |
173 | } | |
174 | } | |
175 | ||
176 | stale_count = 0; | |
177 | igrp_retry: | |
621f3374 DW |
178 | error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, |
179 | &igrplen); | |
372d4ba9 DW |
180 | } |
181 | ||
182 | err: | |
183 | if (error) { | |
621f3374 | 184 | str_liberror(ctx, error, descr); |
372d4ba9 DW |
185 | moveon = false; |
186 | } | |
187 | out: | |
188 | return moveon; | |
189 | } | |
190 | ||
191 | /* BULKSTAT wrapper routines. */ | |
192 | struct xfs_scan_inodes { | |
193 | xfs_inode_iter_fn fn; | |
194 | void *arg; | |
195 | bool moveon; | |
196 | }; | |
197 | ||
198 | /* Scan all the inodes in an AG. */ | |
199 | static void | |
200 | xfs_scan_ag_inodes( | |
201 | struct workqueue *wq, | |
202 | xfs_agnumber_t agno, | |
203 | void *arg) | |
204 | { | |
205 | struct xfs_scan_inodes *si = arg; | |
206 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
207 | char descr[DESCR_BUFSZ]; | |
208 | uint64_t ag_ino; | |
209 | uint64_t next_ag_ino; | |
210 | bool moveon; | |
211 | ||
212 | snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u inodes"), | |
213 | major(ctx->fsinfo.fs_datadev), | |
214 | minor(ctx->fsinfo.fs_datadev), | |
215 | agno); | |
216 | ||
a749451c DW |
217 | ag_ino = cvt_agino_to_ino(&ctx->mnt, agno, 0); |
218 | next_ag_ino = cvt_agino_to_ino(&ctx->mnt, agno + 1, 0); | |
372d4ba9 DW |
219 | |
220 | moveon = xfs_iterate_inodes_range(ctx, descr, ctx->fshandle, ag_ino, | |
221 | next_ag_ino - 1, si->fn, si->arg); | |
222 | if (!moveon) | |
223 | si->moveon = false; | |
224 | } | |
225 | ||
226 | /* Scan all the inodes in a filesystem. */ | |
227 | bool | |
228 | xfs_scan_all_inodes( | |
229 | struct scrub_ctx *ctx, | |
230 | xfs_inode_iter_fn fn, | |
231 | void *arg) | |
232 | { | |
233 | struct xfs_scan_inodes si; | |
234 | xfs_agnumber_t agno; | |
235 | struct workqueue wq; | |
236 | int ret; | |
237 | ||
238 | si.moveon = true; | |
239 | si.fn = fn; | |
240 | si.arg = arg; | |
241 | ||
242 | ret = workqueue_create(&wq, (struct xfs_mount *)ctx, | |
243 | scrub_nproc_workqueue(ctx)); | |
244 | if (ret) { | |
82377bde | 245 | str_info(ctx, ctx->mntpoint, _("Could not create workqueue.")); |
372d4ba9 DW |
246 | return false; |
247 | } | |
248 | ||
3f9efb2e | 249 | for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) { |
372d4ba9 DW |
250 | ret = workqueue_add(&wq, xfs_scan_ag_inodes, agno, &si); |
251 | if (ret) { | |
252 | si.moveon = false; | |
82377bde | 253 | str_info(ctx, ctx->mntpoint, |
372d4ba9 DW |
254 | _("Could not queue AG %u bulkstat work."), agno); |
255 | break; | |
256 | } | |
257 | } | |
258 | ||
259 | workqueue_destroy(&wq); | |
260 | ||
261 | return si.moveon; | |
262 | } | |
263 | ||
264 | /* | |
265 | * Open a file by handle, or return a negative error code. | |
266 | */ | |
267 | int | |
268 | xfs_open_handle( | |
269 | struct xfs_handle *handle) | |
270 | { | |
271 | return open_by_fshandle(handle, sizeof(*handle), | |
272 | O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY); | |
273 | } |