]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
604dd334 DW |
2 | /* |
3 | * Copyright (C) 2018 Oracle. All Rights Reserved. | |
604dd334 | 4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> |
604dd334 | 5 | */ |
a440f877 | 6 | #include "xfs.h" |
604dd334 DW |
7 | #include <stdint.h> |
8 | #include <stdlib.h> | |
9 | #include <sys/statvfs.h> | |
10 | #include "platform_defs.h" | |
604dd334 | 11 | #include "xfs_arch.h" |
604dd334 | 12 | #include "xfs_format.h" |
42b4c8e8 | 13 | #include "libfrog/paths.h" |
56598728 | 14 | #include "libfrog/workqueue.h" |
604dd334 DW |
15 | #include "xfs_scrub.h" |
16 | #include "common.h" | |
17 | #include "fscounters.h" | |
621f3374 | 18 | #include "libfrog/bulkstat.h" |
604dd334 DW |
19 | |
20 | /* | |
21 | * Filesystem counter collection routines. We can count the number of | |
22 | * inodes in the filesystem, and we can estimate the block counters. | |
23 | */ | |
24 | ||
25 | /* Count the number of inodes in the filesystem. */ | |
26 | ||
27 | /* INUMBERS wrapper routines. */ | |
28 | struct xfs_count_inodes { | |
29 | bool moveon; | |
30 | uint64_t counters[0]; | |
31 | }; | |
32 | ||
33 | /* | |
34 | * Count the number of inodes. Use INUMBERS to figure out how many inodes | |
35 | * exist in the filesystem, assuming we've already scrubbed that. | |
36 | */ | |
37 | static bool | |
38 | xfs_count_inodes_range( | |
39 | struct scrub_ctx *ctx, | |
40 | const char *descr, | |
41 | uint64_t first_ino, | |
42 | uint64_t last_ino, | |
43 | uint64_t *count) | |
44 | { | |
604dd334 | 45 | struct xfs_inogrp inogrp; |
621f3374 | 46 | uint64_t igrp_ino; |
604dd334 | 47 | uint64_t nr = 0; |
621f3374 | 48 | uint32_t igrplen = 0; |
604dd334 DW |
49 | int error; |
50 | ||
51 | ASSERT(!(first_ino & (XFS_INODES_PER_CHUNK - 1))); | |
52 | ASSERT((last_ino & (XFS_INODES_PER_CHUNK - 1))); | |
53 | ||
604dd334 | 54 | igrp_ino = first_ino; |
621f3374 DW |
55 | while (!(error = xfrog_inumbers(&ctx->mnt, &igrp_ino, 1, &inogrp, |
56 | &igrplen))) { | |
57 | if (igrplen == 0 || inogrp.xi_startino >= last_ino) | |
58 | break; | |
604dd334 | 59 | nr += inogrp.xi_alloccount; |
604dd334 DW |
60 | } |
61 | ||
62 | if (error) { | |
621f3374 | 63 | str_liberror(ctx, error, descr); |
604dd334 DW |
64 | return false; |
65 | } | |
66 | ||
67 | *count = nr; | |
68 | return true; | |
69 | } | |
70 | ||
71 | /* Scan all the inodes in an AG. */ | |
72 | static void | |
73 | xfs_count_ag_inodes( | |
74 | struct workqueue *wq, | |
75 | xfs_agnumber_t agno, | |
76 | void *arg) | |
77 | { | |
78 | struct xfs_count_inodes *ci = arg; | |
79 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
80 | char descr[DESCR_BUFSZ]; | |
81 | uint64_t ag_ino; | |
82 | uint64_t next_ag_ino; | |
83 | bool moveon; | |
84 | ||
85 | snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u inodes"), | |
86 | major(ctx->fsinfo.fs_datadev), | |
87 | minor(ctx->fsinfo.fs_datadev), | |
88 | agno); | |
89 | ||
a749451c DW |
90 | ag_ino = cvt_agino_to_ino(&ctx->mnt, agno, 0); |
91 | next_ag_ino = cvt_agino_to_ino(&ctx->mnt, agno + 1, 0); | |
604dd334 DW |
92 | |
93 | moveon = xfs_count_inodes_range(ctx, descr, ag_ino, next_ag_ino - 1, | |
94 | &ci->counters[agno]); | |
95 | if (!moveon) | |
96 | ci->moveon = false; | |
97 | } | |
98 | ||
99 | /* Count all the inodes in a filesystem. */ | |
100 | bool | |
101 | xfs_count_all_inodes( | |
102 | struct scrub_ctx *ctx, | |
103 | uint64_t *count) | |
104 | { | |
105 | struct xfs_count_inodes *ci; | |
106 | xfs_agnumber_t agno; | |
107 | struct workqueue wq; | |
108 | bool moveon; | |
109 | int ret; | |
110 | ||
111 | ci = calloc(1, sizeof(struct xfs_count_inodes) + | |
3f9efb2e | 112 | (ctx->mnt.fsgeom.agcount * sizeof(uint64_t))); |
604dd334 DW |
113 | if (!ci) |
114 | return false; | |
115 | ci->moveon = true; | |
116 | ||
117 | ret = workqueue_create(&wq, (struct xfs_mount *)ctx, | |
118 | scrub_nproc_workqueue(ctx)); | |
119 | if (ret) { | |
120 | moveon = false; | |
82377bde | 121 | str_info(ctx, ctx->mntpoint, _("Could not create workqueue.")); |
604dd334 DW |
122 | goto out_free; |
123 | } | |
3f9efb2e | 124 | for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) { |
604dd334 DW |
125 | ret = workqueue_add(&wq, xfs_count_ag_inodes, agno, ci); |
126 | if (ret) { | |
127 | moveon = false; | |
82377bde | 128 | str_info(ctx, ctx->mntpoint, |
604dd334 DW |
129 | _("Could not queue AG %u icount work."), agno); |
130 | break; | |
131 | } | |
132 | } | |
133 | workqueue_destroy(&wq); | |
134 | ||
3f9efb2e | 135 | for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) |
604dd334 DW |
136 | *count += ci->counters[agno]; |
137 | moveon = ci->moveon; | |
138 | ||
139 | out_free: | |
140 | free(ci); | |
141 | return moveon; | |
142 | } | |
143 | ||
144 | /* Estimate the number of blocks and inodes in the filesystem. */ | |
145 | bool | |
146 | xfs_scan_estimate_blocks( | |
147 | struct scrub_ctx *ctx, | |
148 | unsigned long long *d_blocks, | |
149 | unsigned long long *d_bfree, | |
150 | unsigned long long *r_blocks, | |
151 | unsigned long long *r_bfree, | |
152 | unsigned long long *f_files, | |
153 | unsigned long long *f_free) | |
154 | { | |
155 | struct xfs_fsop_counts fc; | |
156 | struct xfs_fsop_resblks rb; | |
157 | struct statvfs sfs; | |
158 | int error; | |
159 | ||
160 | /* Grab the fstatvfs counters, since it has to report accurately. */ | |
3f9efb2e | 161 | error = fstatvfs(ctx->mnt.fd, &sfs); |
604dd334 DW |
162 | if (error) { |
163 | str_errno(ctx, ctx->mntpoint); | |
164 | return false; | |
165 | } | |
166 | ||
167 | /* Fetch the filesystem counters. */ | |
3f9efb2e | 168 | error = ioctl(ctx->mnt.fd, XFS_IOC_FSCOUNTS, &fc); |
604dd334 DW |
169 | if (error) { |
170 | str_errno(ctx, ctx->mntpoint); | |
171 | return false; | |
172 | } | |
173 | ||
174 | /* | |
175 | * XFS reserves some blocks to prevent hard ENOSPC, so add those | |
176 | * blocks back to the free data counts. | |
177 | */ | |
3f9efb2e | 178 | error = ioctl(ctx->mnt.fd, XFS_IOC_GET_RESBLKS, &rb); |
604dd334 DW |
179 | if (error) |
180 | str_errno(ctx, ctx->mntpoint); | |
181 | sfs.f_bfree += rb.resblks_avail; | |
182 | ||
3f9efb2e DW |
183 | *d_blocks = sfs.f_blocks; |
184 | if (ctx->mnt.fsgeom.logstart > 0) | |
185 | *d_blocks += ctx->mnt.fsgeom.logblocks; | |
604dd334 | 186 | *d_bfree = sfs.f_bfree; |
3f9efb2e | 187 | *r_blocks = ctx->mnt.fsgeom.rtblocks; |
604dd334 DW |
188 | *r_bfree = fc.freertx; |
189 | *f_files = sfs.f_files; | |
190 | *f_free = sfs.f_ffree; | |
191 | ||
192 | return true; | |
193 | } |