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>
10 #include "libfrog/paths.h"
11 #include "libfrog/ptvar.h"
13 #include "xfs_scrub.h"
16 #include "fscounters.h"
20 /* Phase 7: Check summary counters. */
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 */
29 /* Record block usage. */
32 struct scrub_ctx
*ctx
,
36 struct summary_counts
*counts
;
37 unsigned long long len
;
40 counts
= ptvar_get((struct ptvar
*)arg
, &ret
);
42 str_liberror(ctx
, -ret
, _("retrieving summary counts"));
45 if (fsmap
->fmr_device
== ctx
->fsinfo
.fs_logdev
)
47 if ((fsmap
->fmr_flags
& FMR_OF_SPECIAL_OWNER
) &&
48 fsmap
->fmr_owner
== XFS_FMR_OWN_FREE
)
51 len
= fsmap
->fmr_length
;
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
;
58 if (fsmap
->fmr_device
== ctx
->fsinfo
.fs_rtdev
) {
59 /* Count realtime extents. */
60 counts
->rbytes
+= len
;
62 /* Count datadev extents. */
63 if (counts
->next_phys
>= fsmap
->fmr_physical
+ len
)
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
;
74 /* Add all the summaries in the per-thread counter */
81 struct summary_counts
*total
= arg
;
82 struct summary_counts
*item
= data
;
84 total
->dbytes
+= item
->dbytes
;
85 total
->rbytes
+= item
->rbytes
;
86 total
->agbytes
+= item
->agbytes
;
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
98 struct scrub_ctx
*ctx
)
100 struct summary_counts totalcount
= {0};
101 struct action_list alist
;
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
;
118 /* Check and fix the fs summary counters. */
119 action_list_init(&alist
);
120 error
= scrub_fs_summary(ctx
, &alist
);
123 error
= action_list_process(ctx
, ctx
->mnt
.fd
, &alist
,
124 ALP_COMPLAIN_IF_UNFIXED
| ALP_NOPROGRESS
);
128 /* Flush everything out to disk before we start counting. */
129 error
= syncfs(ctx
->mnt
.fd
);
131 str_errno(ctx
, ctx
->mntpoint
);
135 error
= -ptvar_alloc(scrub_nproc(ctx
), sizeof(struct summary_counts
),
138 str_liberror(ctx
, error
, _("setting up block counter"));
142 /* Use fsmap to count blocks. */
143 error
= scrub_scan_all_spacemaps(ctx
, count_block_summary
, ptvar
);
146 error
= -ptvar_foreach(ptvar
, add_summaries
, &totalcount
);
148 str_liberror(ctx
, error
, _("counting blocks"));
153 /* Scan the whole fs. */
154 error
= scrub_count_all_inodes(ctx
, &counted_inodes
);
156 str_liberror(ctx
, error
, _("counting inodes"));
160 error
= scrub_scan_estimate_blocks(ctx
, &d_blocks
, &d_bfree
, &r_blocks
,
161 &r_bfree
, &used_files
);
163 str_liberror(ctx
, error
, _("estimating verify work"));
168 * If we counted blocks with fsmap, then dblocks includes
169 * blocks for the AGFL and the freespace/rmap btrees. The
170 * filesystem treats them as "free", but since we scanned
171 * them, we'll consider them used.
173 d_bfree
-= cvt_b_to_off_fsbt(&ctx
->mnt
, totalcount
.agbytes
);
175 /* Report on what we found. */
176 used_data
= cvt_off_fsb_to_b(&ctx
->mnt
, d_blocks
- d_bfree
);
177 used_rt
= cvt_off_fsb_to_b(&ctx
->mnt
, r_blocks
- r_bfree
);
178 stat_data
= totalcount
.dbytes
;
179 stat_rt
= totalcount
.rbytes
;
182 * Complain if the counts are off by more than 10% unless
183 * the inaccuracy is less than 32MB worth of blocks or 100 inodes.
185 absdiff
= 1ULL << 25;
187 complain
|= !within_range(ctx
, stat_data
, used_data
, absdiff
, 1, 10,
189 complain
|= !within_range(ctx
, stat_rt
, used_rt
, absdiff
, 1, 10,
190 _("realtime blocks"));
191 complain
|= !within_range(ctx
, counted_inodes
, used_files
, 100, 1, 10,
198 if (used_rt
|| stat_rt
) {
199 d
= auto_space_units(used_data
, &du
);
200 r
= auto_space_units(used_rt
, &ru
);
201 i
= auto_units(used_files
, &iu
, &ip
);
203 _("%.1f%s data used; %.1f%s realtime data used; %.*f%s inodes used.\n"),
204 d
, du
, r
, ru
, ip
, i
, iu
);
205 d
= auto_space_units(stat_data
, &du
);
206 r
= auto_space_units(stat_rt
, &ru
);
207 i
= auto_units(counted_inodes
, &iu
, &ip
);
209 _("%.1f%s data found; %.1f%s realtime data found; %.*f%s inodes found.\n"),
210 d
, du
, r
, ru
, ip
, i
, iu
);
212 d
= auto_space_units(used_data
, &du
);
213 i
= auto_units(used_files
, &iu
, &ip
);
215 _("%.1f%s data used; %.*f%s inodes used.\n"),
217 d
= auto_space_units(stat_data
, &du
);
218 i
= auto_units(counted_inodes
, &iu
, &ip
);
220 _("%.1f%s data found; %.*f%s inodes found.\n"),
227 * Complain if the checked inode counts are off, which
228 * implies an incomplete check.
231 !within_range(ctx
, counted_inodes
, ctx
->inodes_checked
, 100, 1, 10,
232 _("checked inodes"))) {
237 i1
= auto_units(counted_inodes
, &i1u
, &i1p
);
238 i2
= auto_units(ctx
->inodes_checked
, &i2u
, &i2p
);
240 _("%.*f%s inodes counted; %.*f%s inodes checked.\n"),
241 i1p
, i1
, i1u
, i2p
, i2
, i2u
);
246 * Complain if the checked block counts are off, which
247 * implies an incomplete check.
249 if (ctx
->bytes_checked
&&
251 !within_range(ctx
, used_data
+ used_rt
,
252 ctx
->bytes_checked
, absdiff
, 1, 10,
253 _("verified blocks")))) {
257 b1
= auto_space_units(used_data
+ used_rt
, &b1u
);
258 b2
= auto_space_units(ctx
->bytes_checked
, &b2u
);
260 _("%.1f%s data counted; %.1f%s data verified.\n"),