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