]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
bc94c5d6 DW |
2 | /* |
3 | * Copyright (C) 2018 Oracle. All Rights Reserved. | |
bc94c5d6 | 4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> |
bc94c5d6 | 5 | */ |
a440f877 | 6 | #include "xfs.h" |
bc94c5d6 DW |
7 | #include <stdint.h> |
8 | #include <string.h> | |
9 | #include <pthread.h> | |
10 | #include <sys/statvfs.h> | |
56598728 | 11 | #include "libfrog/workqueue.h" |
42b4c8e8 | 12 | #include "libfrog/paths.h" |
bc94c5d6 DW |
13 | #include "xfs_scrub.h" |
14 | #include "common.h" | |
15 | #include "spacemap.h" | |
16 | ||
17 | /* | |
18 | * Filesystem space map iterators. | |
19 | * | |
20 | * Logically, we call GETFSMAP to fetch a set of space map records and | |
21 | * call a function to iterate over the records. However, that's not | |
22 | * what actually happens -- the work is split into separate items, with | |
23 | * each AG, the realtime device, and the log device getting their own | |
24 | * work items. For an XFS with a realtime device and an external log, | |
25 | * this means that we can have up to ($agcount + 2) threads running at | |
26 | * once. | |
27 | * | |
28 | * This comes into play if we want to have per-workitem memory. Maybe. | |
29 | * XXX: do we really need all that ? | |
30 | */ | |
31 | ||
32 | #define FSMAP_NR 65536 | |
33 | ||
34 | /* Iterate all the fs block mappings between the two keys. */ | |
35 | bool | |
36 | xfs_iterate_fsmap( | |
37 | struct scrub_ctx *ctx, | |
38 | const char *descr, | |
39 | struct fsmap *keys, | |
40 | xfs_fsmap_iter_fn fn, | |
41 | void *arg) | |
42 | { | |
43 | struct fsmap_head *head; | |
44 | struct fsmap *p; | |
45 | bool moveon = true; | |
46 | int i; | |
47 | int error; | |
48 | ||
49 | head = malloc(fsmap_sizeof(FSMAP_NR)); | |
50 | if (!head) { | |
51 | str_errno(ctx, descr); | |
52 | return false; | |
53 | } | |
54 | ||
55 | memset(head, 0, sizeof(*head)); | |
56 | memcpy(head->fmh_keys, keys, sizeof(struct fsmap) * 2); | |
57 | head->fmh_count = FSMAP_NR; | |
58 | ||
3f9efb2e | 59 | while ((error = ioctl(ctx->mnt.fd, FS_IOC_GETFSMAP, head)) == 0) { |
bc94c5d6 DW |
60 | for (i = 0, p = head->fmh_recs; |
61 | i < head->fmh_entries; | |
62 | i++, p++) { | |
63 | moveon = fn(ctx, descr, p, arg); | |
64 | if (!moveon) | |
65 | goto out; | |
66 | if (xfs_scrub_excessive_errors(ctx)) { | |
67 | moveon = false; | |
68 | goto out; | |
69 | } | |
70 | } | |
71 | ||
72 | if (head->fmh_entries == 0) | |
73 | break; | |
74 | p = &head->fmh_recs[head->fmh_entries - 1]; | |
75 | if (p->fmr_flags & FMR_OF_LAST) | |
76 | break; | |
77 | fsmap_advance(head); | |
78 | } | |
79 | ||
80 | if (error) { | |
81 | str_errno(ctx, descr); | |
82 | moveon = false; | |
83 | } | |
84 | out: | |
85 | free(head); | |
86 | return moveon; | |
87 | } | |
88 | ||
89 | /* GETFSMAP wrappers routines. */ | |
90 | struct xfs_scan_blocks { | |
91 | xfs_fsmap_iter_fn fn; | |
92 | void *arg; | |
93 | bool moveon; | |
94 | }; | |
95 | ||
96 | /* Iterate all the reverse mappings of an AG. */ | |
97 | static void | |
98 | xfs_scan_ag_blocks( | |
99 | struct workqueue *wq, | |
100 | xfs_agnumber_t agno, | |
101 | void *arg) | |
102 | { | |
103 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
104 | struct xfs_scan_blocks *sbx = arg; | |
105 | char descr[DESCR_BUFSZ]; | |
106 | struct fsmap keys[2]; | |
107 | off64_t bperag; | |
108 | bool moveon; | |
109 | ||
3f9efb2e DW |
110 | bperag = (off64_t)ctx->mnt.fsgeom.agblocks * |
111 | (off64_t)ctx->mnt.fsgeom.blocksize; | |
bc94c5d6 DW |
112 | |
113 | snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u fsmap"), | |
114 | major(ctx->fsinfo.fs_datadev), | |
115 | minor(ctx->fsinfo.fs_datadev), | |
116 | agno); | |
117 | ||
118 | memset(keys, 0, sizeof(struct fsmap) * 2); | |
119 | keys->fmr_device = ctx->fsinfo.fs_datadev; | |
120 | keys->fmr_physical = agno * bperag; | |
121 | (keys + 1)->fmr_device = ctx->fsinfo.fs_datadev; | |
122 | (keys + 1)->fmr_physical = ((agno + 1) * bperag) - 1; | |
123 | (keys + 1)->fmr_owner = ULLONG_MAX; | |
124 | (keys + 1)->fmr_offset = ULLONG_MAX; | |
125 | (keys + 1)->fmr_flags = UINT_MAX; | |
126 | ||
127 | moveon = xfs_iterate_fsmap(ctx, descr, keys, sbx->fn, sbx->arg); | |
128 | if (!moveon) | |
129 | sbx->moveon = false; | |
130 | } | |
131 | ||
132 | /* Iterate all the reverse mappings of a standalone device. */ | |
133 | static void | |
134 | xfs_scan_dev_blocks( | |
135 | struct scrub_ctx *ctx, | |
136 | int idx, | |
137 | dev_t dev, | |
138 | struct xfs_scan_blocks *sbx) | |
139 | { | |
140 | struct fsmap keys[2]; | |
141 | char descr[DESCR_BUFSZ]; | |
142 | bool moveon; | |
143 | ||
144 | snprintf(descr, DESCR_BUFSZ, _("dev %d:%d fsmap"), | |
145 | major(dev), minor(dev)); | |
146 | ||
147 | memset(keys, 0, sizeof(struct fsmap) * 2); | |
148 | keys->fmr_device = dev; | |
149 | (keys + 1)->fmr_device = dev; | |
150 | (keys + 1)->fmr_physical = ULLONG_MAX; | |
151 | (keys + 1)->fmr_owner = ULLONG_MAX; | |
152 | (keys + 1)->fmr_offset = ULLONG_MAX; | |
153 | (keys + 1)->fmr_flags = UINT_MAX; | |
154 | ||
155 | moveon = xfs_iterate_fsmap(ctx, descr, keys, sbx->fn, sbx->arg); | |
156 | if (!moveon) | |
157 | sbx->moveon = false; | |
158 | } | |
159 | ||
160 | /* Iterate all the reverse mappings of the realtime device. */ | |
161 | static void | |
162 | xfs_scan_rt_blocks( | |
163 | struct workqueue *wq, | |
164 | xfs_agnumber_t agno, | |
165 | void *arg) | |
166 | { | |
167 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
168 | ||
169 | xfs_scan_dev_blocks(ctx, agno, ctx->fsinfo.fs_rtdev, arg); | |
170 | } | |
171 | ||
172 | /* Iterate all the reverse mappings of the log device. */ | |
173 | static void | |
174 | xfs_scan_log_blocks( | |
175 | struct workqueue *wq, | |
176 | xfs_agnumber_t agno, | |
177 | void *arg) | |
178 | { | |
179 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
180 | ||
181 | xfs_scan_dev_blocks(ctx, agno, ctx->fsinfo.fs_logdev, arg); | |
182 | } | |
183 | ||
184 | /* Scan all the blocks in a filesystem. */ | |
185 | bool | |
186 | xfs_scan_all_spacemaps( | |
187 | struct scrub_ctx *ctx, | |
188 | xfs_fsmap_iter_fn fn, | |
189 | void *arg) | |
190 | { | |
191 | struct workqueue wq; | |
192 | struct xfs_scan_blocks sbx; | |
193 | xfs_agnumber_t agno; | |
194 | int ret; | |
195 | ||
196 | sbx.moveon = true; | |
197 | sbx.fn = fn; | |
198 | sbx.arg = arg; | |
199 | ||
200 | ret = workqueue_create(&wq, (struct xfs_mount *)ctx, | |
201 | scrub_nproc_workqueue(ctx)); | |
202 | if (ret) { | |
9d57cbfc | 203 | str_liberror(ctx, ret, _("creating fsmap workqueue")); |
bc94c5d6 DW |
204 | return false; |
205 | } | |
206 | if (ctx->fsinfo.fs_rt) { | |
207 | ret = workqueue_add(&wq, xfs_scan_rt_blocks, | |
3f9efb2e | 208 | ctx->mnt.fsgeom.agcount + 1, &sbx); |
bc94c5d6 DW |
209 | if (ret) { |
210 | sbx.moveon = false; | |
9d57cbfc | 211 | str_liberror(ctx, ret, _("queueing rtdev fsmap work")); |
bc94c5d6 DW |
212 | goto out; |
213 | } | |
214 | } | |
215 | if (ctx->fsinfo.fs_log) { | |
216 | ret = workqueue_add(&wq, xfs_scan_log_blocks, | |
3f9efb2e | 217 | ctx->mnt.fsgeom.agcount + 2, &sbx); |
bc94c5d6 DW |
218 | if (ret) { |
219 | sbx.moveon = false; | |
9d57cbfc | 220 | str_liberror(ctx, ret, _("queueing logdev fsmap work")); |
bc94c5d6 DW |
221 | goto out; |
222 | } | |
223 | } | |
3f9efb2e | 224 | for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) { |
bc94c5d6 DW |
225 | ret = workqueue_add(&wq, xfs_scan_ag_blocks, agno, &sbx); |
226 | if (ret) { | |
227 | sbx.moveon = false; | |
9d57cbfc | 228 | str_liberror(ctx, ret, _("queueing per-AG fsmap work")); |
bc94c5d6 DW |
229 | break; |
230 | } | |
231 | } | |
232 | out: | |
233 | workqueue_destroy(&wq); | |
234 | ||
235 | return sbx.moveon; | |
236 | } |