]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/phase6.c
xfs_scrub: don't report media errors for space with unknowable owner
[thirdparty/xfsprogs-dev.git] / scrub / phase6.c
CommitLineData
8d318d62 1// SPDX-License-Identifier: GPL-2.0-or-later
b364a9c0 2/*
52520522 3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
8d318d62 4 * Author: Darrick J. Wong <djwong@kernel.org>
b364a9c0 5 */
a440f877 6#include "xfs.h"
b364a9c0 7#include <stdint.h>
b364a9c0
DW
8#include <dirent.h>
9#include <sys/statvfs.h>
9b72515a 10#include <linux/fsmap.h>
b364a9c0 11#include "handle.h"
42b4c8e8 12#include "libfrog/paths.h"
56598728 13#include "libfrog/workqueue.h"
b364a9c0
DW
14#include "xfs_scrub.h"
15#include "common.h"
a58400ed 16#include "libfrog/bitmap.h"
b364a9c0
DW
17#include "disk.h"
18#include "filemap.h"
ed60d210 19#include "fscounters.h"
b364a9c0
DW
20#include "inodes.h"
21#include "read_verify.h"
22#include "spacemap.h"
23#include "vfs.h"
24
25/*
26 * Phase 6: Verify data file integrity.
27 *
28 * Identify potential data block extents with GETFSMAP, then feed those
29 * extents to the read-verify pool to get the verify commands batched,
30 * issued, and (if there are problems) reported back to us. If there
31 * are errors, we'll record the bad regions and (if available) use rmap
32 * to tell us if metadata are now corrupt. Otherwise, we'll scan the
33 * whole directory tree looking for files that overlap the bad regions
34 * and report the paths of the now corrupt files.
35 */
36
f1bb1696
DW
37/* Verify disk blocks with GETFSMAP */
38
557f98d7 39struct media_verify_state {
f1bb1696
DW
40 struct read_verify_pool *rvp_data;
41 struct read_verify_pool *rvp_log;
42 struct read_verify_pool *rvp_realtime;
43 struct bitmap *d_bad; /* bytes */
44 struct bitmap *r_bad; /* bytes */
45};
46
b364a9c0 47/* Find the fd for a given device identifier. */
f1bb1696 48static struct read_verify_pool *
af9eb208 49dev_to_pool(
f1bb1696 50 struct scrub_ctx *ctx,
557f98d7 51 struct media_verify_state *vs,
f1bb1696 52 dev_t dev)
b364a9c0
DW
53{
54 if (dev == ctx->fsinfo.fs_datadev)
557f98d7 55 return vs->rvp_data;
b364a9c0 56 else if (dev == ctx->fsinfo.fs_logdev)
557f98d7 57 return vs->rvp_log;
b364a9c0 58 else if (dev == ctx->fsinfo.fs_rtdev)
557f98d7 59 return vs->rvp_realtime;
b364a9c0
DW
60 abort();
61}
62
63/* Find the device major/minor for a given file descriptor. */
64static dev_t
af9eb208 65disk_to_dev(
b364a9c0
DW
66 struct scrub_ctx *ctx,
67 struct disk *disk)
68{
69 if (disk == ctx->datadev)
70 return ctx->fsinfo.fs_datadev;
71 else if (disk == ctx->logdev)
72 return ctx->fsinfo.fs_logdev;
73 else if (disk == ctx->rtdev)
74 return ctx->fsinfo.fs_rtdev;
75 abort();
76}
77
c9b349bd
DW
78/* Find the incore bad blocks bitmap for a given disk. */
79static struct bitmap *
80bitmap_for_disk(
81 struct scrub_ctx *ctx,
82 struct disk *disk,
83 struct media_verify_state *vs)
84{
af9eb208 85 dev_t dev = disk_to_dev(ctx, disk);
c9b349bd
DW
86
87 if (dev == ctx->fsinfo.fs_datadev)
88 return vs->d_bad;
89 else if (dev == ctx->fsinfo.fs_rtdev)
90 return vs->r_bad;
91 return NULL;
92}
93
94struct disk_ioerr_report {
95 struct scrub_ctx *ctx;
96 struct disk *disk;
97};
98
b364a9c0
DW
99struct owner_decode {
100 uint64_t owner;
101 const char *descr;
102};
103
104static const struct owner_decode special_owners[] = {
105 {XFS_FMR_OWN_FREE, "free space"},
106 {XFS_FMR_OWN_UNKNOWN, "unknown owner"},
107 {XFS_FMR_OWN_FS, "static FS metadata"},
108 {XFS_FMR_OWN_LOG, "journalling log"},
109 {XFS_FMR_OWN_AG, "per-AG metadata"},
110 {XFS_FMR_OWN_INOBT, "inode btree blocks"},
111 {XFS_FMR_OWN_INODES, "inodes"},
112 {XFS_FMR_OWN_REFC, "refcount btree"},
113 {XFS_FMR_OWN_COW, "CoW staging"},
114 {XFS_FMR_OWN_DEFECTIVE, "bad blocks"},
115 {0, NULL},
116};
117
118/* Decode a special owner. */
119static const char *
af9eb208 120decode_special_owner(
b364a9c0
DW
121 uint64_t owner)
122{
123 const struct owner_decode *od = special_owners;
124
125 while (od->descr) {
126 if (od->owner == owner)
127 return od->descr;
128 od++;
129 }
130
131 return NULL;
132}
133
134/* Routines to translate bad physical extents into file paths and offsets. */
135
ed953d26 136struct badfile_report {
73ce9669
DW
137 struct scrub_ctx *ctx;
138 const char *descr;
139 struct media_verify_state *vs;
140 struct file_bmap *bmap;
ed953d26
DW
141};
142
143/* Report on bad extents found during a media scan. */
144static int
145report_badfile(
146 uint64_t start,
147 uint64_t length,
148 void *arg)
149{
150 struct badfile_report *br = arg;
151 unsigned long long bad_offset;
152 unsigned long long bad_length;
153
154 /* Clamp the bad region to the file mapping. */
155 if (start < br->bmap->bm_physical) {
156 length -= br->bmap->bm_physical - start;
157 start = br->bmap->bm_physical;
158 }
159 length = min(length, br->bmap->bm_length);
160
161 /* Figure out how far into the bmap is the bad mapping and report it. */
162 bad_offset = start - br->bmap->bm_physical;
163 bad_length = min(start + length,
164 br->bmap->bm_physical + br->bmap->bm_length) - start;
165
49e05cb0 166 str_unfixable_error(br->ctx, br->descr,
ed953d26
DW
167_("media error at data offset %llu length %llu."),
168 br->bmap->bm_offset + bad_offset, bad_length);
169 return 0;
170}
171
b364a9c0 172/* Report if this extent overlaps a bad region. */
73ce9669 173static int
663e02a0 174report_data_loss(
b364a9c0 175 struct scrub_ctx *ctx,
b364a9c0
DW
176 int fd,
177 int whichfork,
178 struct fsxattr *fsx,
73ce9669 179 struct file_bmap *bmap,
b364a9c0
DW
180 void *arg)
181{
73ce9669
DW
182 struct badfile_report *br = arg;
183 struct media_verify_state *vs = br->vs;
b364a9c0 184 struct bitmap *bmp;
73ce9669
DW
185
186 br->bmap = bmap;
b364a9c0
DW
187
188 /* Only report errors for real extents. */
189 if (bmap->bm_flags & (BMV_OF_PREALLOC | BMV_OF_DELALLOC))
73ce9669 190 return 0;
b364a9c0
DW
191
192 if (fsx->fsx_xflags & FS_XFLAG_REALTIME)
ed5f9cc7 193 bmp = vs->r_bad;
b364a9c0 194 else
ed5f9cc7 195 bmp = vs->d_bad;
b364a9c0 196
93d69bc7 197 return -bitmap_iterate_range(bmp, bmap->bm_physical, bmap->bm_length,
73ce9669 198 report_badfile, br);
b364a9c0
DW
199}
200
663e02a0 201/* Report if the extended attribute data overlaps a bad region. */
73ce9669 202static int
663e02a0
DW
203report_attr_loss(
204 struct scrub_ctx *ctx,
663e02a0
DW
205 int fd,
206 int whichfork,
207 struct fsxattr *fsx,
73ce9669 208 struct file_bmap *bmap,
663e02a0
DW
209 void *arg)
210{
73ce9669
DW
211 struct badfile_report *br = arg;
212 struct media_verify_state *vs = br->vs;
663e02a0
DW
213 struct bitmap *bmp = vs->d_bad;
214
215 /* Complain about attr fork extents that don't look right. */
216 if (bmap->bm_flags & (BMV_OF_PREALLOC | BMV_OF_DELALLOC)) {
73ce9669 217 str_info(ctx, br->descr,
663e02a0 218_("found unexpected unwritten/delalloc attr fork extent."));
73ce9669 219 return 0;
663e02a0
DW
220 }
221
222 if (fsx->fsx_xflags & FS_XFLAG_REALTIME) {
73ce9669 223 str_info(ctx, br->descr,
663e02a0 224_("found unexpected realtime attr fork extent."));
73ce9669 225 return 0;
663e02a0
DW
226 }
227
228 if (bitmap_test(bmp, bmap->bm_physical, bmap->bm_length))
73ce9669 229 str_corrupt(ctx, br->descr,
663e02a0
DW
230_("media error in extended attribute data."));
231
73ce9669 232 return 0;
663e02a0
DW
233}
234
b364a9c0 235/* Iterate the extent mappings of a file to report errors. */
af9eb208
DW
236static int
237report_fd_loss(
b364a9c0
DW
238 struct scrub_ctx *ctx,
239 const char *descr,
240 int fd,
241 void *arg)
242{
73ce9669
DW
243 struct badfile_report br = {
244 .ctx = ctx,
245 .vs = arg,
246 .descr = descr,
247 };
248 struct file_bmap key = {0};
249 int ret;
b364a9c0
DW
250
251 /* data fork */
73ce9669
DW
252 ret = scrub_iterate_filemaps(ctx, fd, XFS_DATA_FORK, &key,
253 report_data_loss, &br);
254 if (ret) {
255 str_liberror(ctx, ret, descr);
af9eb208 256 return ret;
73ce9669 257 }
b364a9c0
DW
258
259 /* attr fork */
73ce9669
DW
260 ret = scrub_iterate_filemaps(ctx, fd, XFS_ATTR_FORK, &key,
261 report_attr_loss, &br);
262 if (ret) {
263 str_liberror(ctx, ret, descr);
af9eb208 264 return ret;
73ce9669 265 }
af9eb208
DW
266
267 return 0;
b364a9c0
DW
268}
269
270/* Report read verify errors in unlinked (but still open) files. */
271static int
af9eb208 272report_inode_loss(
b364a9c0
DW
273 struct scrub_ctx *ctx,
274 struct xfs_handle *handle,
4cca629d 275 struct xfs_bulkstat *bstat,
b364a9c0
DW
276 void *arg)
277{
278 char descr[DESCR_BUFSZ];
b364a9c0 279 int fd;
af9eb208 280 int error, err2;
b364a9c0 281
b364a9c0
DW
282 /* Ignore linked files and things we can't open. */
283 if (bstat->bs_nlink != 0)
284 return 0;
285 if (!S_ISREG(bstat->bs_mode) && !S_ISDIR(bstat->bs_mode))
286 return 0;
287
15589f0a
DW
288 scrub_render_ino_descr(ctx, descr, DESCR_BUFSZ,
289 bstat->bs_ino, bstat->bs_gen, _("(unlinked)"));
290
b364a9c0 291 /* Try to open the inode. */
59f79e0a 292 fd = scrub_open_handle(handle);
b364a9c0
DW
293 if (fd < 0) {
294 error = errno;
295 if (error == ESTALE)
296 return error;
297
bb5dbd06
DW
298 str_info(ctx, descr,
299_("Disappeared during read error reporting."));
b364a9c0
DW
300 return error;
301 }
302
303 /* Go find the badness. */
af9eb208
DW
304 error = report_fd_loss(ctx, descr, fd, arg);
305
306 err2 = close(fd);
307 if (err2)
6c05cc5d 308 str_errno(ctx, descr);
b364a9c0 309
af9eb208 310 return error;
b364a9c0
DW
311}
312
313/* Scan a directory for matches in the read verify error list. */
f544ec31 314static int
af9eb208 315report_dir_loss(
b364a9c0
DW
316 struct scrub_ctx *ctx,
317 const char *path,
318 int dir_fd,
319 void *arg)
320{
af9eb208 321 return report_fd_loss(ctx, path, dir_fd, arg);
b364a9c0
DW
322}
323
324/*
325 * Scan the inode associated with a directory entry for matches with
326 * the read verify error list.
327 */
f544ec31 328static int
af9eb208 329report_dirent_loss(
b364a9c0
DW
330 struct scrub_ctx *ctx,
331 const char *path,
332 int dir_fd,
333 struct dirent *dirent,
334 struct stat *sb,
335 void *arg)
336{
b364a9c0 337 int fd;
af9eb208 338 int error, err2;
b364a9c0
DW
339
340 /* Ignore things we can't open. */
341 if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode))
f544ec31 342 return 0;
b364a9c0
DW
343
344 /* Ignore . and .. */
345 if (!strcmp(".", dirent->d_name) || !strcmp("..", dirent->d_name))
f544ec31 346 return 0;
b364a9c0
DW
347
348 /*
349 * If we were given a dirent, open the associated file under
350 * dir_fd for badblocks scanning. If dirent is NULL, then it's
351 * the directory itself we want to scan.
352 */
353 fd = openat(dir_fd, dirent->d_name,
354 O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
f544ec31
DW
355 if (fd < 0) {
356 if (errno == ENOENT)
357 return 0;
358 str_errno(ctx, path);
359 return errno;
360 }
b364a9c0
DW
361
362 /* Go find the badness. */
af9eb208 363 error = report_fd_loss(ctx, path, fd, arg);
b364a9c0 364
af9eb208
DW
365 err2 = close(fd);
366 if (err2)
6c05cc5d 367 str_errno(ctx, path);
af9eb208
DW
368 if (!error && err2)
369 error = err2;
370
371 return error;
b364a9c0
DW
372}
373
c9b349bd 374/* Use a fsmap to report metadata lost to a media error. */
7a2eef2b 375static int
c9b349bd 376report_ioerr_fsmap(
b364a9c0 377 struct scrub_ctx *ctx,
b364a9c0
DW
378 struct fsmap *map,
379 void *arg)
380{
381 const char *type;
f1f5fd3a 382 char buf[DESCR_BUFSZ];
b364a9c0
DW
383 uint64_t err_physical = *(uint64_t *)arg;
384 uint64_t err_off;
385
909c6a54
DW
386 /* Don't care about unwritten extents. */
387 if (map->fmr_flags & FMR_OF_PREALLOC)
7a2eef2b 388 return 0;
909c6a54 389
b364a9c0
DW
390 if (err_physical > map->fmr_physical)
391 err_off = err_physical - map->fmr_physical;
392 else
393 err_off = 0;
394
f1f5fd3a 395 /* Report special owners */
b364a9c0 396 if (map->fmr_flags & FMR_OF_SPECIAL_OWNER) {
f1f5fd3a
DW
397 snprintf(buf, DESCR_BUFSZ, _("disk offset %"PRIu64),
398 (uint64_t)map->fmr_physical + err_off);
af9eb208 399 type = decode_special_owner(map->fmr_owner);
96ac83c8
DW
400 /*
401 * On filesystems that don't store reverse mappings, the
402 * GETFSMAP call returns OWNER_UNKNOWN for allocated space.
403 * We'll have to let the directory tree walker find the file
404 * that lost data.
405 */
406 if (!(ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_RMAPBT) &&
407 map->fmr_owner == XFS_FMR_OWN_UNKNOWN) {
408 str_info(ctx, buf, _("media error detected."));
409 } else {
410 str_corrupt(ctx, buf, _("media error in %s."), type);
411 }
b364a9c0
DW
412 }
413
02d0069e
DW
414 /* Report extent maps */
415 if (map->fmr_flags & FMR_OF_EXTENT_MAP) {
416 bool attr = (map->fmr_flags & FMR_OF_ATTR_FORK);
417
418 scrub_render_ino_descr(ctx, buf, DESCR_BUFSZ,
419 map->fmr_owner, 0, " %s",
420 attr ? _("extended attribute") :
421 _("file data"));
abc2e70d 422 str_corrupt(ctx, buf, _("media error in extent map"));
02d0069e
DW
423 }
424
b364a9c0
DW
425 /*
426 * XXX: If we had a getparent() call we could report IO errors
427 * efficiently. Until then, we'll have to scan the dir tree
428 * to find the bad file's pathname.
429 */
430
7a2eef2b 431 return 0;
b364a9c0
DW
432}
433
434/*
c9b349bd
DW
435 * For a range of bad blocks, visit each space mapping that overlaps the bad
436 * range so that we can report lost metadata.
b364a9c0 437 */
c9b349bd
DW
438static int
439report_ioerr(
b364a9c0
DW
440 uint64_t start,
441 uint64_t length,
b364a9c0
DW
442 void *arg)
443{
444 struct fsmap keys[2];
c9b349bd 445 struct disk_ioerr_report *dioerr = arg;
b364a9c0 446 dev_t dev;
b364a9c0 447
af9eb208 448 dev = disk_to_dev(dioerr->ctx, dioerr->disk);
b364a9c0 449
b364a9c0
DW
450 /* Go figure out which blocks are bad from the fsmap. */
451 memset(keys, 0, sizeof(struct fsmap) * 2);
452 keys->fmr_device = dev;
453 keys->fmr_physical = start;
454 (keys + 1)->fmr_device = dev;
455 (keys + 1)->fmr_physical = start + length - 1;
456 (keys + 1)->fmr_owner = ULLONG_MAX;
457 (keys + 1)->fmr_offset = ULLONG_MAX;
458 (keys + 1)->fmr_flags = UINT_MAX;
93d69bc7 459 return -scrub_iterate_fsmap(dioerr->ctx, keys, report_ioerr_fsmap,
b364a9c0 460 &start);
c9b349bd
DW
461}
462
463/* Report all the media errors found on a disk. */
464static int
465report_disk_ioerrs(
466 struct scrub_ctx *ctx,
467 struct disk *disk,
468 struct media_verify_state *vs)
469{
470 struct disk_ioerr_report dioerr = {
471 .ctx = ctx,
472 .disk = disk,
473 };
474 struct bitmap *tree;
475
476 if (!disk)
477 return 0;
478 tree = bitmap_for_disk(ctx, disk, vs);
479 if (!tree)
480 return 0;
93d69bc7 481 return -bitmap_iterate(tree, report_ioerr, &dioerr);
c9b349bd
DW
482}
483
484/* Given bad extent lists for the data & rtdev, find bad files. */
af9eb208 485static int
c9b349bd
DW
486report_all_media_errors(
487 struct scrub_ctx *ctx,
488 struct media_verify_state *vs)
489{
c9b349bd
DW
490 int ret;
491
492 ret = report_disk_ioerrs(ctx, ctx->datadev, vs);
493 if (ret) {
494 str_liberror(ctx, ret, _("walking datadev io errors"));
af9eb208 495 return ret;
c9b349bd
DW
496 }
497
498 ret = report_disk_ioerrs(ctx, ctx->rtdev, vs);
499 if (ret) {
500 str_liberror(ctx, ret, _("walking rtdev io errors"));
af9eb208 501 return ret;
c9b349bd
DW
502 }
503
504 /* Scan the directory tree to get file paths. */
af9eb208 505 ret = scan_fs_tree(ctx, report_dir_loss, report_dirent_loss, vs);
f544ec31 506 if (ret)
af9eb208 507 return ret;
c9b349bd
DW
508
509 /* Scan for unlinked files. */
af9eb208 510 return scrub_scan_all_inodes(ctx, report_inode_loss, vs);
b364a9c0
DW
511}
512
513/* Schedule a read-verify of a (data block) extent. */
7a2eef2b
DW
514static int
515check_rmap(
b364a9c0 516 struct scrub_ctx *ctx,
b364a9c0
DW
517 struct fsmap *map,
518 void *arg)
519{
557f98d7 520 struct media_verify_state *vs = arg;
f1bb1696 521 struct read_verify_pool *rvp;
8cab77d3 522 int ret;
f1bb1696 523
af9eb208 524 rvp = dev_to_pool(ctx, vs, map->fmr_device);
b364a9c0
DW
525
526 dbg_printf("rmap dev %d:%d phys %"PRIu64" owner %"PRId64
527 " offset %"PRIu64" len %"PRIu64" flags 0x%x\n",
528 major(map->fmr_device), minor(map->fmr_device),
529 (uint64_t)map->fmr_physical, (int64_t)map->fmr_owner,
530 (uint64_t)map->fmr_offset, (uint64_t)map->fmr_length,
531 map->fmr_flags);
532
533 /* "Unknown" extents should be verified; they could be data. */
534 if ((map->fmr_flags & FMR_OF_SPECIAL_OWNER) &&
535 map->fmr_owner == XFS_FMR_OWN_UNKNOWN)
536 map->fmr_flags &= ~FMR_OF_SPECIAL_OWNER;
537
538 /*
539 * We only care about read-verifying data extents that have been
540 * written to disk. This means we can skip "special" owners
541 * (metadata), xattr blocks, unwritten extents, and extent maps.
542 * These should all get checked elsewhere in the scrubber.
543 */
544 if (map->fmr_flags & (FMR_OF_PREALLOC | FMR_OF_ATTR_FORK |
545 FMR_OF_EXTENT_MAP | FMR_OF_SPECIAL_OWNER))
7a2eef2b 546 return 0;
b364a9c0
DW
547
548 /* XXX: Filter out directory data blocks. */
549
550 /* Schedule the read verify command for (eventual) running. */
8cab77d3
DW
551 ret = read_verify_schedule_io(rvp, map->fmr_physical, map->fmr_length,
552 vs);
553 if (ret) {
7a2eef2b
DW
554 str_liberror(ctx, ret, _("scheduling media verify command"));
555 return ret;
8cab77d3 556 }
b364a9c0 557
7a2eef2b 558 return 0;
b364a9c0
DW
559}
560
f1bb1696 561/* Wait for read/verify actions to finish, then return # bytes checked. */
8cab77d3 562static int
f1bb1696 563clean_pool(
8cab77d3
DW
564 struct read_verify_pool *rvp,
565 unsigned long long *bytes_checked)
f1bb1696 566{
8cab77d3
DW
567 uint64_t pool_checked;
568 int ret;
f1bb1696
DW
569
570 if (!rvp)
571 return 0;
572
22d658ec
DW
573 ret = read_verify_force_io(rvp);
574 if (ret)
575 return ret;
576
8cab77d3
DW
577 ret = read_verify_pool_flush(rvp);
578 if (ret)
579 goto out_destroy;
580
581 ret = read_verify_bytes(rvp, &pool_checked);
582 if (ret)
583 goto out_destroy;
584
585 *bytes_checked += pool_checked;
586out_destroy:
f1bb1696
DW
587 read_verify_pool_destroy(rvp);
588 return ret;
589}
590
c9b349bd
DW
591/* Remember a media error for later. */
592static void
593remember_ioerr(
594 struct scrub_ctx *ctx,
595 struct disk *disk,
596 uint64_t start,
597 uint64_t length,
598 int error,
599 void *arg)
600{
601 struct media_verify_state *vs = arg;
602 struct bitmap *tree;
603 int ret;
604
605 tree = bitmap_for_disk(ctx, disk, vs);
606 if (!tree) {
607 str_liberror(ctx, ENOENT, _("finding bad block bitmap"));
608 return;
609 }
610
93d69bc7 611 ret = -bitmap_set(tree, start, length);
c9b349bd
DW
612 if (ret)
613 str_liberror(ctx, ret, _("setting bad block bitmap"));
614}
615
b364a9c0
DW
616/*
617 * Read verify all the file data blocks in a filesystem. Since XFS doesn't
618 * do data checksums, we trust that the underlying storage will pass back
619 * an IO error if it can't retrieve whatever we previously stored there.
620 * If we hit an IO error, we'll record the bad blocks in a bitmap and then
621 * scan the extent maps of the entire fs tree to figure (and the unlinked
622 * inodes) out which files are now broken.
623 */
af9eb208
DW
624int
625phase6_func(
b364a9c0
DW
626 struct scrub_ctx *ctx)
627{
557f98d7 628 struct media_verify_state vs = { NULL };
af9eb208 629 int ret, ret2, ret3;
b364a9c0 630
93d69bc7 631 ret = -bitmap_alloc(&vs.d_bad);
233fabee
DW
632 if (ret) {
633 str_liberror(ctx, ret, _("creating datadev badblock bitmap"));
af9eb208 634 return ret;
b364a9c0
DW
635 }
636
93d69bc7 637 ret = -bitmap_alloc(&vs.r_bad);
233fabee
DW
638 if (ret) {
639 str_liberror(ctx, ret, _("creating realtime badblock bitmap"));
b364a9c0
DW
640 goto out_dbad;
641 }
642
8cab77d3 643 ret = read_verify_pool_alloc(ctx, ctx->datadev,
c9b349bd 644 ctx->mnt.fsgeom.blocksize, remember_ioerr,
8cab77d3
DW
645 scrub_nproc(ctx), &vs.rvp_data);
646 if (ret) {
647 str_liberror(ctx, ret, _("creating datadev media verifier"));
b364a9c0
DW
648 goto out_rbad;
649 }
f1bb1696 650 if (ctx->logdev) {
8cab77d3 651 ret = read_verify_pool_alloc(ctx, ctx->logdev,
c9b349bd 652 ctx->mnt.fsgeom.blocksize, remember_ioerr,
8cab77d3
DW
653 scrub_nproc(ctx), &vs.rvp_log);
654 if (ret) {
655 str_liberror(ctx, ret,
656 _("creating logdev media verifier"));
f1bb1696
DW
657 goto out_datapool;
658 }
659 }
660 if (ctx->rtdev) {
8cab77d3 661 ret = read_verify_pool_alloc(ctx, ctx->rtdev,
c9b349bd 662 ctx->mnt.fsgeom.blocksize, remember_ioerr,
8cab77d3
DW
663 scrub_nproc(ctx), &vs.rvp_realtime);
664 if (ret) {
665 str_liberror(ctx, ret,
666 _("creating rtdev media verifier"));
f1bb1696
DW
667 goto out_logpool;
668 }
669 }
7a2eef2b 670 ret = scrub_scan_all_spacemaps(ctx, check_rmap, &vs);
af9eb208 671 if (ret)
f1bb1696 672 goto out_rtpool;
8cab77d3
DW
673
674 ret = clean_pool(vs.rvp_data, &ctx->bytes_checked);
af9eb208 675 if (ret)
8cab77d3 676 str_liberror(ctx, ret, _("flushing datadev verify pool"));
8cab77d3 677
af9eb208
DW
678 ret2 = clean_pool(vs.rvp_log, &ctx->bytes_checked);
679 if (ret2)
680 str_liberror(ctx, ret2, _("flushing logdev verify pool"));
8cab77d3 681
af9eb208
DW
682 ret3 = clean_pool(vs.rvp_realtime, &ctx->bytes_checked);
683 if (ret3)
684 str_liberror(ctx, ret3, _("flushing rtdev verify pool"));
685
686 /*
687 * If the verify flush didn't work or we found no bad blocks, we're
688 * done! No errors detected.
689 */
690 if (ret || ret2 || ret3)
691 goto out_rbad;
692 if (bitmap_empty(vs.d_bad) && bitmap_empty(vs.r_bad))
693 goto out_rbad;
b364a9c0
DW
694
695 /* Scan the whole dir tree to see what matches the bad extents. */
af9eb208 696 ret = report_all_media_errors(ctx, &vs);
b364a9c0 697
557f98d7
DW
698 bitmap_free(&vs.r_bad);
699 bitmap_free(&vs.d_bad);
af9eb208 700 return ret;
b364a9c0 701
f1bb1696 702out_rtpool:
7668d01d 703 if (vs.rvp_realtime) {
4cd869e5 704 read_verify_pool_abort(vs.rvp_realtime);
557f98d7 705 read_verify_pool_destroy(vs.rvp_realtime);
7668d01d 706 }
f1bb1696 707out_logpool:
7668d01d 708 if (vs.rvp_log) {
4cd869e5 709 read_verify_pool_abort(vs.rvp_log);
557f98d7 710 read_verify_pool_destroy(vs.rvp_log);
7668d01d 711 }
f1bb1696 712out_datapool:
4cd869e5 713 read_verify_pool_abort(vs.rvp_data);
557f98d7 714 read_verify_pool_destroy(vs.rvp_data);
b364a9c0 715out_rbad:
557f98d7 716 bitmap_free(&vs.r_bad);
b364a9c0 717out_dbad:
557f98d7 718 bitmap_free(&vs.d_bad);
af9eb208 719 return ret;
b364a9c0 720}
ed60d210 721
af9eb208
DW
722/* Estimate how much work we're going to do. */
723int
724phase6_estimate(
ed60d210
DW
725 struct scrub_ctx *ctx,
726 uint64_t *items,
727 unsigned int *nr_threads,
728 int *rshift)
729{
730 unsigned long long d_blocks;
731 unsigned long long d_bfree;
732 unsigned long long r_blocks;
733 unsigned long long r_bfree;
0b78ac05 734 unsigned long long dontcare;
934d8d3a 735 int ret;
ed60d210 736
0b78ac05
DW
737 ret = scrub_scan_estimate_blocks(ctx, &d_blocks, &d_bfree, &r_blocks,
738 &r_bfree, &dontcare);
934d8d3a
DW
739 if (ret) {
740 str_liberror(ctx, ret, _("estimating verify work"));
af9eb208 741 return ret;
934d8d3a 742 }
ed60d210 743
a749451c
DW
744 *items = cvt_off_fsb_to_b(&ctx->mnt,
745 (d_blocks - d_bfree) + (r_blocks - r_bfree));
ed60d210
DW
746 *nr_threads = disk_heads(ctx->datadev);
747 *rshift = 20;
af9eb208
DW
748 return 0;
749}