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