]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/phase7.c
xfs_scrub_all: fix argument passing when invoking xfs_scrub manually
[thirdparty/xfsprogs-dev.git] / scrub / phase7.c
CommitLineData
8d318d62 1// SPDX-License-Identifier: GPL-2.0-or-later
698c6c7c 2/*
52520522 3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
8d318d62 4 * Author: Darrick J. Wong <djwong@kernel.org>
698c6c7c 5 */
a440f877 6#include "xfs.h"
698c6c7c 7#include <stdint.h>
698c6c7c
DW
8#include <stdlib.h>
9#include <sys/statvfs.h>
9b72515a 10#include <linux/fsmap.h>
42b4c8e8 11#include "libfrog/paths.h"
14051909 12#include "libfrog/ptvar.h"
cbaf1c9d 13#include "list.h"
698c6c7c
DW
14#include "xfs_scrub.h"
15#include "common.h"
cbaf1c9d 16#include "scrub.h"
698c6c7c
DW
17#include "fscounters.h"
18#include "spacemap.h"
cbaf1c9d 19#include "repair.h"
698c6c7c
DW
20
21/* Phase 7: Check summary counters. */
22
593f2ab8 23struct summary_counts {
698c6c7c
DW
24 unsigned long long dbytes; /* data dev bytes */
25 unsigned long long rbytes; /* rt dev bytes */
26 unsigned long long next_phys; /* next phys bytes we see? */
27 unsigned long long agbytes; /* freespace bytes */
28};
29
30/* Record block usage. */
7a2eef2b
DW
31static int
32count_block_summary(
593f2ab8 33 struct scrub_ctx *ctx,
593f2ab8
DW
34 struct fsmap *fsmap,
35 void *arg)
698c6c7c 36{
593f2ab8
DW
37 struct summary_counts *counts;
38 unsigned long long len;
cb321a39 39 int ret;
698c6c7c 40
cb321a39
DW
41 counts = ptvar_get((struct ptvar *)arg, &ret);
42 if (ret) {
2f4422f4
DW
43 str_liberror(ctx, -ret, _("retrieving summary counts"));
44 return -ret;
cb321a39 45 }
698c6c7c 46 if (fsmap->fmr_device == ctx->fsinfo.fs_logdev)
7a2eef2b 47 return 0;
698c6c7c
DW
48 if ((fsmap->fmr_flags & FMR_OF_SPECIAL_OWNER) &&
49 fsmap->fmr_owner == XFS_FMR_OWN_FREE)
7a2eef2b 50 return 0;
698c6c7c
DW
51
52 len = fsmap->fmr_length;
53
54 /* freesp btrees live in free space, need to adjust counters later. */
55 if ((fsmap->fmr_flags & FMR_OF_SPECIAL_OWNER) &&
56 fsmap->fmr_owner == XFS_FMR_OWN_AG) {
57 counts->agbytes += fsmap->fmr_length;
58 }
59 if (fsmap->fmr_device == ctx->fsinfo.fs_rtdev) {
60 /* Count realtime extents. */
61 counts->rbytes += len;
62 } else {
63 /* Count datadev extents. */
64 if (counts->next_phys >= fsmap->fmr_physical + len)
7a2eef2b 65 return 0;
698c6c7c
DW
66 else if (counts->next_phys > fsmap->fmr_physical)
67 len = counts->next_phys - fsmap->fmr_physical;
68 counts->dbytes += len;
69 counts->next_phys = fsmap->fmr_physical + fsmap->fmr_length;
70 }
71
7a2eef2b 72 return 0;
698c6c7c
DW
73}
74
75/* Add all the summaries in the per-thread counter */
cb321a39 76static int
0d96df9d 77add_summaries(
593f2ab8
DW
78 struct ptvar *ptv,
79 void *data,
80 void *arg)
698c6c7c 81{
593f2ab8
DW
82 struct summary_counts *total = arg;
83 struct summary_counts *item = data;
698c6c7c
DW
84
85 total->dbytes += item->dbytes;
86 total->rbytes += item->rbytes;
87 total->agbytes += item->agbytes;
cb321a39 88 return 0;
698c6c7c
DW
89}
90
91/*
92 * Count all inodes and blocks in the filesystem as told by GETFSMAP and
93 * BULKSTAT, and compare that to summary counters. Since this is a live
94 * filesystem we'll be content if the summary counts are within 10% of
95 * what we observed.
96 */
0d96df9d
DW
97int
98phase7_func(
593f2ab8 99 struct scrub_ctx *ctx)
698c6c7c 100{
593f2ab8 101 struct summary_counts totalcount = {0};
83d2c80b 102 struct action_list alist;
593f2ab8
DW
103 struct ptvar *ptvar;
104 unsigned long long used_data;
105 unsigned long long used_rt;
106 unsigned long long used_files;
107 unsigned long long stat_data;
108 unsigned long long stat_rt;
109 uint64_t counted_inodes = 0;
110 unsigned long long absdiff;
111 unsigned long long d_blocks;
112 unsigned long long d_bfree;
113 unsigned long long r_blocks;
114 unsigned long long r_bfree;
593f2ab8
DW
115 bool complain;
116 int ip;
117 int error;
698c6c7c 118
cbaf1c9d 119 /* Check and fix the fs summary counters. */
83d2c80b 120 action_list_init(&alist);
273165cc 121 error = scrub_fs_summary(ctx, &alist);
d22f2471 122 if (error)
0d96df9d 123 return error;
bb9be147 124 error = action_list_process(ctx, -1, &alist,
cbaf1c9d 125 ALP_COMPLAIN_IF_UNFIXED | ALP_NOPROGRESS);
83d2c80b 126 if (error)
0d96df9d 127 return error;
cbaf1c9d 128
698c6c7c 129 /* Flush everything out to disk before we start counting. */
3f9efb2e 130 error = syncfs(ctx->mnt.fd);
698c6c7c
DW
131 if (error) {
132 str_errno(ctx, ctx->mntpoint);
0d96df9d 133 return error;
698c6c7c
DW
134 }
135
2f4422f4 136 error = -ptvar_alloc(scrub_nproc(ctx), sizeof(struct summary_counts),
cb321a39
DW
137 &ptvar);
138 if (error) {
139 str_liberror(ctx, error, _("setting up block counter"));
0d96df9d 140 return error;
698c6c7c
DW
141 }
142
143 /* Use fsmap to count blocks. */
7a2eef2b 144 error = scrub_scan_all_spacemaps(ctx, count_block_summary, ptvar);
0d96df9d 145 if (error)
698c6c7c 146 goto out_free;
2f4422f4 147 error = -ptvar_foreach(ptvar, add_summaries, &totalcount);
cb321a39
DW
148 if (error) {
149 str_liberror(ctx, error, _("counting blocks"));
698c6c7c 150 goto out_free;
cb321a39 151 }
698c6c7c
DW
152 ptvar_free(ptvar);
153
154 /* Scan the whole fs. */
934d8d3a
DW
155 error = scrub_count_all_inodes(ctx, &counted_inodes);
156 if (error) {
157 str_liberror(ctx, error, _("counting inodes"));
0d96df9d 158 return error;
934d8d3a 159 }
698c6c7c 160
934d8d3a 161 error = scrub_scan_estimate_blocks(ctx, &d_blocks, &d_bfree, &r_blocks,
0b78ac05 162 &r_bfree, &used_files);
934d8d3a
DW
163 if (error) {
164 str_liberror(ctx, error, _("estimating verify work"));
0d96df9d 165 return error;
934d8d3a 166 }
698c6c7c
DW
167
168 /*
169 * If we counted blocks with fsmap, then dblocks includes
170 * blocks for the AGFL and the freespace/rmap btrees. The
171 * filesystem treats them as "free", but since we scanned
172 * them, we'll consider them used.
173 */
a749451c 174 d_bfree -= cvt_b_to_off_fsbt(&ctx->mnt, totalcount.agbytes);
698c6c7c
DW
175
176 /* Report on what we found. */
a749451c
DW
177 used_data = cvt_off_fsb_to_b(&ctx->mnt, d_blocks - d_bfree);
178 used_rt = cvt_off_fsb_to_b(&ctx->mnt, r_blocks - r_bfree);
698c6c7c
DW
179 stat_data = totalcount.dbytes;
180 stat_rt = totalcount.rbytes;
181
182 /*
183 * Complain if the counts are off by more than 10% unless
184 * the inaccuracy is less than 32MB worth of blocks or 100 inodes.
185 */
186 absdiff = 1ULL << 25;
187 complain = verbose;
188 complain |= !within_range(ctx, stat_data, used_data, absdiff, 1, 10,
189 _("data blocks"));
190 complain |= !within_range(ctx, stat_rt, used_rt, absdiff, 1, 10,
191 _("realtime blocks"));
192 complain |= !within_range(ctx, counted_inodes, used_files, 100, 1, 10,
193 _("inodes"));
194
195 if (complain) {
196 double d, r, i;
197 char *du, *ru, *iu;
198
199 if (used_rt || stat_rt) {
200 d = auto_space_units(used_data, &du);
201 r = auto_space_units(used_rt, &ru);
058f45da 202 i = auto_units(used_files, &iu, &ip);
698c6c7c 203 fprintf(stdout,
058f45da
DW
204_("%.1f%s data used; %.1f%s realtime data used; %.*f%s inodes used.\n"),
205 d, du, r, ru, ip, i, iu);
698c6c7c
DW
206 d = auto_space_units(stat_data, &du);
207 r = auto_space_units(stat_rt, &ru);
058f45da 208 i = auto_units(counted_inodes, &iu, &ip);
698c6c7c 209 fprintf(stdout,
058f45da
DW
210_("%.1f%s data found; %.1f%s realtime data found; %.*f%s inodes found.\n"),
211 d, du, r, ru, ip, i, iu);
698c6c7c
DW
212 } else {
213 d = auto_space_units(used_data, &du);
058f45da 214 i = auto_units(used_files, &iu, &ip);
698c6c7c 215 fprintf(stdout,
058f45da
DW
216_("%.1f%s data used; %.*f%s inodes used.\n"),
217 d, du, ip, i, iu);
698c6c7c 218 d = auto_space_units(stat_data, &du);
058f45da 219 i = auto_units(counted_inodes, &iu, &ip);
698c6c7c 220 fprintf(stdout,
058f45da
DW
221_("%.1f%s data found; %.*f%s inodes found.\n"),
222 d, du, ip, i, iu);
698c6c7c
DW
223 }
224 fflush(stdout);
225 }
226
227 /*
228 * Complain if the checked inode counts are off, which
229 * implies an incomplete check.
230 */
231 if (verbose ||
232 !within_range(ctx, counted_inodes, ctx->inodes_checked, 100, 1, 10,
233 _("checked inodes"))) {
234 double i1, i2;
235 char *i1u, *i2u;
058f45da 236 int i1p, i2p;
698c6c7c 237
058f45da
DW
238 i1 = auto_units(counted_inodes, &i1u, &i1p);
239 i2 = auto_units(ctx->inodes_checked, &i2u, &i2p);
698c6c7c 240 fprintf(stdout,
058f45da
DW
241_("%.*f%s inodes counted; %.*f%s inodes checked.\n"),
242 i1p, i1, i1u, i2p, i2, i2u);
698c6c7c
DW
243 fflush(stdout);
244 }
245
246 /*
247 * Complain if the checked block counts are off, which
248 * implies an incomplete check.
249 */
250 if (ctx->bytes_checked &&
251 (verbose ||
252 !within_range(ctx, used_data + used_rt,
253 ctx->bytes_checked, absdiff, 1, 10,
254 _("verified blocks")))) {
255 double b1, b2;
256 char *b1u, *b2u;
257
258 b1 = auto_space_units(used_data + used_rt, &b1u);
259 b2 = auto_space_units(ctx->bytes_checked, &b2u);
260 fprintf(stdout,
261_("%.1f%s data counted; %.1f%s data verified.\n"),
262 b1, b1u, b2, b2u);
263 fflush(stdout);
264 }
265
0d96df9d 266 return 0;
698c6c7c
DW
267out_free:
268 ptvar_free(ptvar);
0d96df9d
DW
269 return error;
270}