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. */
31 xfs_record_block_summary(
32 struct scrub_ctx
*ctx
,
37 struct summary_counts
*counts
;
38 unsigned long long len
;
40 counts
= ptvar_get((struct ptvar
*)arg
);
41 if (fsmap
->fmr_device
== ctx
->fsinfo
.fs_logdev
)
43 if ((fsmap
->fmr_flags
& FMR_OF_SPECIAL_OWNER
) &&
44 fsmap
->fmr_owner
== XFS_FMR_OWN_FREE
)
47 len
= fsmap
->fmr_length
;
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
;
54 if (fsmap
->fmr_device
== ctx
->fsinfo
.fs_rtdev
) {
55 /* Count realtime extents. */
56 counts
->rbytes
+= len
;
58 /* Count datadev extents. */
59 if (counts
->next_phys
>= fsmap
->fmr_physical
+ len
)
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
;
70 /* Add all the summaries in the per-thread counter */
77 struct summary_counts
*total
= arg
;
78 struct summary_counts
*item
= data
;
80 total
->dbytes
+= item
->dbytes
;
81 total
->rbytes
+= item
->rbytes
;
82 total
->agbytes
+= item
->agbytes
;
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
94 struct scrub_ctx
*ctx
)
96 struct summary_counts totalcount
= {0};
97 struct xfs_action_list alist
;
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
;
117 /* Check and fix the fs summary counters. */
118 xfs_action_list_init(&alist
);
119 moveon
= xfs_scrub_fs_summary(ctx
, &alist
);
122 moveon
= xfs_action_list_process(ctx
, ctx
->mnt
.fd
, &alist
,
123 ALP_COMPLAIN_IF_UNFIXED
| ALP_NOPROGRESS
);
127 /* Flush everything out to disk before we start counting. */
128 error
= syncfs(ctx
->mnt
.fd
);
130 str_errno(ctx
, ctx
->mntpoint
);
134 ptvar
= ptvar_init(scrub_nproc(ctx
), sizeof(struct summary_counts
));
136 str_errno(ctx
, ctx
->mntpoint
);
140 /* Use fsmap to count blocks. */
141 moveon
= xfs_scan_all_spacemaps(ctx
, xfs_record_block_summary
, ptvar
);
144 moveon
= ptvar_foreach(ptvar
, xfs_add_summaries
, &totalcount
);
149 /* Scan the whole fs. */
150 moveon
= xfs_count_all_inodes(ctx
, &counted_inodes
);
154 moveon
= xfs_scan_estimate_blocks(ctx
, &d_blocks
, &d_bfree
, &r_blocks
,
155 &r_bfree
, &f_files
, &f_free
);
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.
165 d_bfree
-= cvt_b_to_off_fsbt(&ctx
->mnt
, totalcount
.agbytes
);
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
;
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.
178 absdiff
= 1ULL << 25;
180 complain
|= !within_range(ctx
, stat_data
, used_data
, absdiff
, 1, 10,
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,
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
);
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
);
202 _("%.1f%s data found; %.1f%s realtime data found; %.*f%s inodes found.\n"),
203 d
, du
, r
, ru
, ip
, i
, iu
);
205 d
= auto_space_units(used_data
, &du
);
206 i
= auto_units(used_files
, &iu
, &ip
);
208 _("%.1f%s data used; %.*f%s inodes used.\n"),
210 d
= auto_space_units(stat_data
, &du
);
211 i
= auto_units(counted_inodes
, &iu
, &ip
);
213 _("%.1f%s data found; %.*f%s inodes found.\n"),
220 * Complain if the checked inode counts are off, which
221 * implies an incomplete check.
224 !within_range(ctx
, counted_inodes
, ctx
->inodes_checked
, 100, 1, 10,
225 _("checked inodes"))) {
230 i1
= auto_units(counted_inodes
, &i1u
, &i1p
);
231 i2
= auto_units(ctx
->inodes_checked
, &i2u
, &i2p
);
233 _("%.*f%s inodes counted; %.*f%s inodes checked.\n"),
234 i1p
, i1
, i1u
, i2p
, i2
, i2u
);
239 * Complain if the checked block counts are off, which
240 * implies an incomplete check.
242 if (ctx
->bytes_checked
&&
244 !within_range(ctx
, used_data
+ used_rt
,
245 ctx
->bytes_checked
, absdiff
, 1, 10,
246 _("verified blocks")))) {
250 b1
= auto_space_units(used_data
+ used_rt
, &b1u
);
251 b2
= auto_space_units(ctx
->bytes_checked
, &b2u
);
253 _("%.1f%s data counted; %.1f%s data verified.\n"),