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