]>
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 | { | |
b94a69ac | 45 | struct xfs_inumbers_req *ireq; |
604dd334 | 46 | uint64_t nr = 0; |
604dd334 DW |
47 | int error; |
48 | ||
49 | ASSERT(!(first_ino & (XFS_INODES_PER_CHUNK - 1))); | |
50 | ASSERT((last_ino & (XFS_INODES_PER_CHUNK - 1))); | |
51 | ||
b94a69ac DW |
52 | ireq = xfrog_inumbers_alloc_req(1, first_ino); |
53 | if (!ireq) { | |
54 | str_info(ctx, descr, _("Insufficient memory; giving up.")); | |
55 | return false; | |
56 | } | |
57 | ||
58 | while (!(error = xfrog_inumbers(&ctx->mnt, ireq))) { | |
59 | if (ireq->hdr.ocount == 0 || | |
60 | ireq->inumbers[0].xi_startino >= last_ino) | |
621f3374 | 61 | break; |
b94a69ac | 62 | nr += ireq->inumbers[0].xi_alloccount; |
604dd334 DW |
63 | } |
64 | ||
b94a69ac DW |
65 | free(ireq); |
66 | ||
604dd334 | 67 | if (error) { |
621f3374 | 68 | str_liberror(ctx, error, descr); |
604dd334 DW |
69 | return false; |
70 | } | |
71 | ||
72 | *count = nr; | |
73 | return true; | |
74 | } | |
75 | ||
76 | /* Scan all the inodes in an AG. */ | |
77 | static void | |
78 | xfs_count_ag_inodes( | |
79 | struct workqueue *wq, | |
80 | xfs_agnumber_t agno, | |
81 | void *arg) | |
82 | { | |
83 | struct xfs_count_inodes *ci = arg; | |
84 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
85 | char descr[DESCR_BUFSZ]; | |
86 | uint64_t ag_ino; | |
87 | uint64_t next_ag_ino; | |
88 | bool moveon; | |
89 | ||
90 | snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u inodes"), | |
91 | major(ctx->fsinfo.fs_datadev), | |
92 | minor(ctx->fsinfo.fs_datadev), | |
93 | agno); | |
94 | ||
a749451c DW |
95 | ag_ino = cvt_agino_to_ino(&ctx->mnt, agno, 0); |
96 | next_ag_ino = cvt_agino_to_ino(&ctx->mnt, agno + 1, 0); | |
604dd334 DW |
97 | |
98 | moveon = xfs_count_inodes_range(ctx, descr, ag_ino, next_ag_ino - 1, | |
99 | &ci->counters[agno]); | |
100 | if (!moveon) | |
101 | ci->moveon = false; | |
102 | } | |
103 | ||
104 | /* Count all the inodes in a filesystem. */ | |
105 | bool | |
106 | xfs_count_all_inodes( | |
107 | struct scrub_ctx *ctx, | |
108 | uint64_t *count) | |
109 | { | |
110 | struct xfs_count_inodes *ci; | |
111 | xfs_agnumber_t agno; | |
112 | struct workqueue wq; | |
113 | bool moveon; | |
114 | int ret; | |
115 | ||
116 | ci = calloc(1, sizeof(struct xfs_count_inodes) + | |
3f9efb2e | 117 | (ctx->mnt.fsgeom.agcount * sizeof(uint64_t))); |
604dd334 DW |
118 | if (!ci) |
119 | return false; | |
120 | ci->moveon = true; | |
121 | ||
122 | ret = workqueue_create(&wq, (struct xfs_mount *)ctx, | |
123 | scrub_nproc_workqueue(ctx)); | |
124 | if (ret) { | |
125 | moveon = false; | |
82377bde | 126 | str_info(ctx, ctx->mntpoint, _("Could not create workqueue.")); |
604dd334 DW |
127 | goto out_free; |
128 | } | |
3f9efb2e | 129 | for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) { |
604dd334 DW |
130 | ret = workqueue_add(&wq, xfs_count_ag_inodes, agno, ci); |
131 | if (ret) { | |
132 | moveon = false; | |
82377bde | 133 | str_info(ctx, ctx->mntpoint, |
604dd334 DW |
134 | _("Could not queue AG %u icount work."), agno); |
135 | break; | |
136 | } | |
137 | } | |
138 | workqueue_destroy(&wq); | |
139 | ||
3f9efb2e | 140 | for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) |
604dd334 DW |
141 | *count += ci->counters[agno]; |
142 | moveon = ci->moveon; | |
143 | ||
144 | out_free: | |
145 | free(ci); | |
146 | return moveon; | |
147 | } | |
148 | ||
149 | /* Estimate the number of blocks and inodes in the filesystem. */ | |
150 | bool | |
151 | xfs_scan_estimate_blocks( | |
152 | struct scrub_ctx *ctx, | |
153 | unsigned long long *d_blocks, | |
154 | unsigned long long *d_bfree, | |
155 | unsigned long long *r_blocks, | |
156 | unsigned long long *r_bfree, | |
157 | unsigned long long *f_files, | |
158 | unsigned long long *f_free) | |
159 | { | |
160 | struct xfs_fsop_counts fc; | |
161 | struct xfs_fsop_resblks rb; | |
162 | struct statvfs sfs; | |
163 | int error; | |
164 | ||
165 | /* Grab the fstatvfs counters, since it has to report accurately. */ | |
3f9efb2e | 166 | error = fstatvfs(ctx->mnt.fd, &sfs); |
604dd334 DW |
167 | if (error) { |
168 | str_errno(ctx, ctx->mntpoint); | |
169 | return false; | |
170 | } | |
171 | ||
172 | /* Fetch the filesystem counters. */ | |
3f9efb2e | 173 | error = ioctl(ctx->mnt.fd, XFS_IOC_FSCOUNTS, &fc); |
604dd334 DW |
174 | if (error) { |
175 | str_errno(ctx, ctx->mntpoint); | |
176 | return false; | |
177 | } | |
178 | ||
179 | /* | |
180 | * XFS reserves some blocks to prevent hard ENOSPC, so add those | |
181 | * blocks back to the free data counts. | |
182 | */ | |
3f9efb2e | 183 | error = ioctl(ctx->mnt.fd, XFS_IOC_GET_RESBLKS, &rb); |
604dd334 DW |
184 | if (error) |
185 | str_errno(ctx, ctx->mntpoint); | |
186 | sfs.f_bfree += rb.resblks_avail; | |
187 | ||
3f9efb2e DW |
188 | *d_blocks = sfs.f_blocks; |
189 | if (ctx->mnt.fsgeom.logstart > 0) | |
190 | *d_blocks += ctx->mnt.fsgeom.logblocks; | |
604dd334 | 191 | *d_bfree = sfs.f_bfree; |
3f9efb2e | 192 | *r_blocks = ctx->mnt.fsgeom.rtblocks; |
604dd334 DW |
193 | *r_bfree = fc.freertx; |
194 | *f_files = sfs.f_files; | |
195 | *f_free = sfs.f_ffree; | |
196 | ||
197 | return true; | |
198 | } |