1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
9 #include <sys/statvfs.h>
12 #include "xfs_scrub.h"
14 #include "fscounters.h"
17 /* Phase 7: Check summary counters. */
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 */
26 /* Record block usage. */
28 xfs_record_block_summary(
29 struct scrub_ctx
*ctx
,
34 struct summary_counts
*counts
;
35 unsigned long long len
;
37 counts
= ptvar_get((struct ptvar
*)arg
);
38 if (fsmap
->fmr_device
== ctx
->fsinfo
.fs_logdev
)
40 if ((fsmap
->fmr_flags
& FMR_OF_SPECIAL_OWNER
) &&
41 fsmap
->fmr_owner
== XFS_FMR_OWN_FREE
)
44 len
= fsmap
->fmr_length
;
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
;
51 if (fsmap
->fmr_device
== ctx
->fsinfo
.fs_rtdev
) {
52 /* Count realtime extents. */
53 counts
->rbytes
+= len
;
55 /* Count datadev extents. */
56 if (counts
->next_phys
>= fsmap
->fmr_physical
+ len
)
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
;
67 /* Add all the summaries in the per-thread counter */
74 struct summary_counts
*total
= arg
;
75 struct summary_counts
*item
= data
;
77 total
->dbytes
+= item
->dbytes
;
78 total
->rbytes
+= item
->rbytes
;
79 total
->agbytes
+= item
->agbytes
;
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
91 struct scrub_ctx
*ctx
)
93 struct summary_counts totalcount
= {0};
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
;
113 /* Flush everything out to disk before we start counting. */
114 error
= syncfs(ctx
->mnt_fd
);
116 str_errno(ctx
, ctx
->mntpoint
);
120 ptvar
= ptvar_init(scrub_nproc(ctx
), sizeof(struct summary_counts
));
122 str_errno(ctx
, ctx
->mntpoint
);
126 /* Use fsmap to count blocks. */
127 moveon
= xfs_scan_all_spacemaps(ctx
, xfs_record_block_summary
, ptvar
);
130 moveon
= ptvar_foreach(ptvar
, xfs_add_summaries
, &totalcount
);
135 /* Scan the whole fs. */
136 moveon
= xfs_count_all_inodes(ctx
, &counted_inodes
);
140 moveon
= xfs_scan_estimate_blocks(ctx
, &d_blocks
, &d_bfree
, &r_blocks
,
141 &r_bfree
, &f_files
, &f_free
);
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.
151 d_bfree
-= totalcount
.agbytes
>> ctx
->blocklog
;
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
;
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.
164 absdiff
= 1ULL << 25;
166 complain
|= !within_range(ctx
, stat_data
, used_data
, absdiff
, 1, 10,
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,
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
);
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
);
188 _("%.1f%s data found; %.1f%s realtime data found; %.*f%s inodes found.\n"),
189 d
, du
, r
, ru
, ip
, i
, iu
);
191 d
= auto_space_units(used_data
, &du
);
192 i
= auto_units(used_files
, &iu
, &ip
);
194 _("%.1f%s data used; %.*f%s inodes used.\n"),
196 d
= auto_space_units(stat_data
, &du
);
197 i
= auto_units(counted_inodes
, &iu
, &ip
);
199 _("%.1f%s data found; %.*f%s inodes found.\n"),
206 * Complain if the checked inode counts are off, which
207 * implies an incomplete check.
210 !within_range(ctx
, counted_inodes
, ctx
->inodes_checked
, 100, 1, 10,
211 _("checked inodes"))) {
216 i1
= auto_units(counted_inodes
, &i1u
, &i1p
);
217 i2
= auto_units(ctx
->inodes_checked
, &i2u
, &i2p
);
219 _("%.*f%s inodes counted; %.*f%s inodes checked.\n"),
220 i1p
, i1
, i1u
, i2p
, i2
, i2u
);
225 * Complain if the checked block counts are off, which
226 * implies an incomplete check.
228 if (ctx
->bytes_checked
&&
230 !within_range(ctx
, used_data
+ used_rt
,
231 ctx
->bytes_checked
, absdiff
, 1, 10,
232 _("verified blocks")))) {
236 b1
= auto_space_units(used_data
+ used_rt
, &b1u
);
237 b2
= auto_space_units(ctx
->bytes_checked
, &b2u
);
239 _("%.1f%s data counted; %.1f%s data verified.\n"),