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