]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/phase3.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
9 #include <sys/statvfs.h>
11 #include "libfrog/paths.h"
12 #include "libfrog/workqueue.h"
13 #include "xfs_scrub.h"
21 /* Phase 3: Scan all inodes. */
23 struct scrub_inode_ctx
{
24 struct scrub_ctx
*ctx
;
26 /* Number of inodes scanned. */
27 struct ptcounter
*icount
;
29 /* per-AG locks to protect the repair lists */
30 pthread_mutex_t
*locks
;
32 /* Set to true to abort all threads. */
35 /* Set to true if we want to defer file repairs to phase 4. */
36 bool always_defer_repairs
;
39 /* Report a filesystem error that the vfs fed us on close. */
42 struct scrub_ctx
*ctx
,
43 struct xfs_bulkstat
*bstat
)
45 char descr
[DESCR_BUFSZ
];
46 int old_errno
= errno
;
48 scrub_render_ino_descr(ctx
, descr
, DESCR_BUFSZ
, bstat
->bs_ino
,
51 str_errno(ctx
, descr
);
55 * Defer all the repairs until phase 4, being careful about locking since the
56 * inode scrub threads are not per-AG.
60 struct scrub_inode_ctx
*ictx
,
62 struct action_list
*alist
)
67 pthread_mutex_lock(&ictx
->locks
[agno
]);
68 action_list_defer(ictx
->ctx
, agno
, alist
);
69 pthread_mutex_unlock(&ictx
->locks
[agno
]);
72 /* Run repair actions now and defer unfinished items for later. */
75 struct scrub_inode_ctx
*ictx
,
78 struct action_list
*alist
)
83 * If at the start of phase 3 we already had ag/rt metadata repairs
84 * queued up for phase 4, leave the action list untouched so that file
85 * metadata repairs will be deferred in scan order until phase 4.
87 if (ictx
->always_defer_repairs
)
90 ret
= action_list_process(ictx
->ctx
, fd
, alist
,
91 ALP_REPAIR_ONLY
| ALP_NOPROGRESS
);
95 defer_inode_repair(ictx
, agno
, alist
);
99 /* Verify the contents, xattrs, and extent maps of an inode. */
102 struct scrub_ctx
*ctx
,
103 struct xfs_handle
*handle
,
104 struct xfs_bulkstat
*bstat
,
107 struct action_list alist
;
108 struct scrub_inode_ctx
*ictx
= arg
;
109 struct ptcounter
*icount
= ictx
->icount
;
114 action_list_init(&alist
);
115 agno
= cvt_ino_to_agno(&ctx
->mnt
, bstat
->bs_ino
);
119 * Open this regular file to pin it in memory. Avoiding the use of
120 * scan-by-handle means that the in-kernel scrubber doesn't pay the
121 * cost of opening the handle (looking up the inode in the inode btree,
122 * grabbing the inode, checking the generation) with every scrub call.
124 * Ignore any runtime or corruption related errors here because we can
125 * fall back to scrubbing by handle. ESTALE can be ignored for the
128 * - If the file has been deleted since bulkstat, there's nothing to
129 * check. Scrub-by-handle returns ENOENT for such inodes.
130 * - If the file has been deleted and reallocated since bulkstat,
131 * its ondisk metadata have been rewritten and is assumed to be ok.
132 * Scrub-by-handle also returns ENOENT if the generation doesn't
134 * - The file itself is corrupt and cannot be loaded. In this case,
135 * we fall back to scrub-by-handle.
137 * Note: We cannot use this same trick for directories because the VFS
138 * will try to reconnect directory file handles to the root directory
139 * by walking '..' entries upwards, and loops in the dirent index
140 * btree will cause livelocks.
142 if (S_ISREG(bstat
->bs_mode
))
143 fd
= scrub_open_handle(handle
);
145 /* Scrub the inode. */
146 error
= scrub_file(ctx
, fd
, bstat
, XFS_SCRUB_TYPE_INODE
, &alist
);
150 error
= try_inode_repair(ictx
, fd
, agno
, &alist
);
154 /* Scrub all block mappings. */
155 error
= scrub_file(ctx
, fd
, bstat
, XFS_SCRUB_TYPE_BMBTD
, &alist
);
158 error
= scrub_file(ctx
, fd
, bstat
, XFS_SCRUB_TYPE_BMBTA
, &alist
);
161 error
= scrub_file(ctx
, fd
, bstat
, XFS_SCRUB_TYPE_BMBTC
, &alist
);
165 error
= try_inode_repair(ictx
, fd
, agno
, &alist
);
169 if (S_ISLNK(bstat
->bs_mode
)) {
170 /* Check symlink contents. */
171 error
= scrub_file(ctx
, fd
, bstat
, XFS_SCRUB_TYPE_SYMLINK
,
173 } else if (S_ISDIR(bstat
->bs_mode
)) {
174 /* Check the directory entries. */
175 error
= scrub_file(ctx
, fd
, bstat
, XFS_SCRUB_TYPE_DIR
, &alist
);
180 /* Check all the extended attributes. */
181 error
= scrub_file(ctx
, fd
, bstat
, XFS_SCRUB_TYPE_XATTR
, &alist
);
185 /* Check parent pointers. */
186 error
= scrub_file(ctx
, fd
, bstat
, XFS_SCRUB_TYPE_PARENT
, &alist
);
190 /* Try to repair the file while it's open. */
191 error
= try_inode_repair(ictx
, fd
, agno
, &alist
);
197 ictx
->aborted
= true;
199 error
= ptcounter_add(icount
, 1);
201 str_liberror(ctx
, error
,
202 _("incrementing scanned inode counter"));
203 ictx
->aborted
= true;
207 if (!error
&& !ictx
->aborted
)
208 defer_inode_repair(ictx
, agno
, &alist
);
215 report_close_error(ctx
, bstat
);
216 ictx
->aborted
= true;
220 if (!error
&& ictx
->aborted
)
225 /* Verify all the inodes in a filesystem. */
228 struct scrub_ctx
*ctx
)
230 struct scrub_inode_ctx ictx
= { .ctx
= ctx
};
235 err
= ptcounter_alloc(scrub_nproc(ctx
), &ictx
.icount
);
237 str_liberror(ctx
, err
, _("creating scanned inode counter"));
241 ictx
.locks
= calloc(ctx
->mnt
.fsgeom
.agcount
, sizeof(pthread_mutex_t
));
243 str_errno(ctx
, _("creating per-AG repair list locks"));
249 * If we already have ag/fs metadata to repair from previous phases,
250 * we would rather not try to repair file metadata until we've tried
251 * to repair the space metadata.
253 for (agno
= 0; agno
< ctx
->mnt
.fsgeom
.agcount
; agno
++) {
254 pthread_mutex_init(&ictx
.locks
[agno
], NULL
);
256 if (!action_list_empty(&ctx
->action_lists
[agno
]))
257 ictx
.always_defer_repairs
= true;
260 err
= scrub_scan_all_inodes(ctx
, scrub_inode
, &ictx
);
261 if (!err
&& ictx
.aborted
)
266 scrub_report_preen_triggers(ctx
);
267 err
= ptcounter_value(ictx
.icount
, &val
);
269 str_liberror(ctx
, err
, _("summing scanned inode counter"));
273 ctx
->inodes_checked
= val
;
275 for (agno
= 0; agno
< ctx
->mnt
.fsgeom
.agcount
; agno
++)
276 pthread_mutex_destroy(&ictx
.locks
[agno
]);
279 ptcounter_free(ictx
.icount
);
283 /* Estimate how much work we're going to do. */
286 struct scrub_ctx
*ctx
,
288 unsigned int *nr_threads
,
291 *items
= ctx
->mnt_sv
.f_files
- ctx
->mnt_sv
.f_ffree
;
292 *nr_threads
= scrub_nproc(ctx
);