]>
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 | ||
7a2eef2b DW |
34 | /* |
35 | * Iterate all the fs block mappings between the two keys. Returns 0 or a | |
36 | * positive error number. | |
37 | */ | |
38 | int | |
39 | scrub_iterate_fsmap( | |
bc94c5d6 | 40 | struct scrub_ctx *ctx, |
bc94c5d6 | 41 | struct fsmap *keys, |
7a2eef2b | 42 | scrub_fsmap_iter_fn fn, |
bc94c5d6 DW |
43 | void *arg) |
44 | { | |
45 | struct fsmap_head *head; | |
46 | struct fsmap *p; | |
bc94c5d6 DW |
47 | int i; |
48 | int error; | |
49 | ||
50 | head = malloc(fsmap_sizeof(FSMAP_NR)); | |
7a2eef2b DW |
51 | if (!head) |
52 | return errno; | |
bc94c5d6 DW |
53 | |
54 | memset(head, 0, sizeof(*head)); | |
55 | memcpy(head->fmh_keys, keys, sizeof(struct fsmap) * 2); | |
56 | head->fmh_count = FSMAP_NR; | |
57 | ||
3f9efb2e | 58 | while ((error = ioctl(ctx->mnt.fd, FS_IOC_GETFSMAP, head)) == 0) { |
bc94c5d6 DW |
59 | for (i = 0, p = head->fmh_recs; |
60 | i < head->fmh_entries; | |
61 | i++, p++) { | |
7a2eef2b DW |
62 | error = fn(ctx, p, arg); |
63 | if (error) | |
bc94c5d6 | 64 | goto out; |
7a2eef2b | 65 | if (xfs_scrub_excessive_errors(ctx)) |
bc94c5d6 | 66 | goto out; |
bc94c5d6 DW |
67 | } |
68 | ||
69 | if (head->fmh_entries == 0) | |
70 | break; | |
71 | p = &head->fmh_recs[head->fmh_entries - 1]; | |
72 | if (p->fmr_flags & FMR_OF_LAST) | |
73 | break; | |
74 | fsmap_advance(head); | |
75 | } | |
7a2eef2b DW |
76 | if (error) |
77 | error = errno; | |
bc94c5d6 DW |
78 | out: |
79 | free(head); | |
7a2eef2b | 80 | return error; |
bc94c5d6 DW |
81 | } |
82 | ||
83 | /* GETFSMAP wrappers routines. */ | |
7a2eef2b DW |
84 | struct scan_blocks { |
85 | scrub_fsmap_iter_fn fn; | |
bc94c5d6 | 86 | void *arg; |
7a2eef2b | 87 | bool aborted; |
bc94c5d6 DW |
88 | }; |
89 | ||
90 | /* Iterate all the reverse mappings of an AG. */ | |
91 | static void | |
7a2eef2b | 92 | scan_ag_rmaps( |
bc94c5d6 DW |
93 | struct workqueue *wq, |
94 | xfs_agnumber_t agno, | |
95 | void *arg) | |
96 | { | |
97 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
7a2eef2b | 98 | struct scan_blocks *sbx = arg; |
bc94c5d6 DW |
99 | struct fsmap keys[2]; |
100 | off64_t bperag; | |
7a2eef2b | 101 | int ret; |
bc94c5d6 | 102 | |
3f9efb2e DW |
103 | bperag = (off64_t)ctx->mnt.fsgeom.agblocks * |
104 | (off64_t)ctx->mnt.fsgeom.blocksize; | |
bc94c5d6 | 105 | |
bc94c5d6 DW |
106 | memset(keys, 0, sizeof(struct fsmap) * 2); |
107 | keys->fmr_device = ctx->fsinfo.fs_datadev; | |
108 | keys->fmr_physical = agno * bperag; | |
109 | (keys + 1)->fmr_device = ctx->fsinfo.fs_datadev; | |
110 | (keys + 1)->fmr_physical = ((agno + 1) * bperag) - 1; | |
111 | (keys + 1)->fmr_owner = ULLONG_MAX; | |
112 | (keys + 1)->fmr_offset = ULLONG_MAX; | |
113 | (keys + 1)->fmr_flags = UINT_MAX; | |
114 | ||
7a2eef2b DW |
115 | if (sbx->aborted) |
116 | return; | |
117 | ||
118 | ret = scrub_iterate_fsmap(ctx, keys, sbx->fn, sbx->arg); | |
119 | if (ret) { | |
120 | char descr[DESCR_BUFSZ]; | |
121 | ||
122 | snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u fsmap"), | |
123 | major(ctx->fsinfo.fs_datadev), | |
124 | minor(ctx->fsinfo.fs_datadev), | |
125 | agno); | |
126 | str_liberror(ctx, ret, descr); | |
127 | sbx->aborted = true; | |
128 | } | |
bc94c5d6 DW |
129 | } |
130 | ||
131 | /* Iterate all the reverse mappings of a standalone device. */ | |
132 | static void | |
7a2eef2b | 133 | scan_dev_rmaps( |
bc94c5d6 DW |
134 | struct scrub_ctx *ctx, |
135 | int idx, | |
136 | dev_t dev, | |
7a2eef2b | 137 | struct scan_blocks *sbx) |
bc94c5d6 DW |
138 | { |
139 | struct fsmap keys[2]; | |
7a2eef2b | 140 | int ret; |
bc94c5d6 DW |
141 | |
142 | memset(keys, 0, sizeof(struct fsmap) * 2); | |
143 | keys->fmr_device = dev; | |
144 | (keys + 1)->fmr_device = dev; | |
145 | (keys + 1)->fmr_physical = ULLONG_MAX; | |
146 | (keys + 1)->fmr_owner = ULLONG_MAX; | |
147 | (keys + 1)->fmr_offset = ULLONG_MAX; | |
148 | (keys + 1)->fmr_flags = UINT_MAX; | |
149 | ||
7a2eef2b DW |
150 | if (sbx->aborted) |
151 | return; | |
152 | ||
153 | ret = scrub_iterate_fsmap(ctx, keys, sbx->fn, sbx->arg); | |
154 | if (ret) { | |
155 | char descr[DESCR_BUFSZ]; | |
156 | ||
157 | snprintf(descr, DESCR_BUFSZ, _("dev %d:%d fsmap"), | |
158 | major(dev), minor(dev)); | |
159 | str_liberror(ctx, ret, descr); | |
160 | sbx->aborted = true; | |
161 | } | |
bc94c5d6 DW |
162 | } |
163 | ||
164 | /* Iterate all the reverse mappings of the realtime device. */ | |
165 | static void | |
7a2eef2b | 166 | scan_rt_rmaps( |
bc94c5d6 DW |
167 | struct workqueue *wq, |
168 | xfs_agnumber_t agno, | |
169 | void *arg) | |
170 | { | |
171 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
172 | ||
7a2eef2b | 173 | scan_dev_rmaps(ctx, agno, ctx->fsinfo.fs_rtdev, arg); |
bc94c5d6 DW |
174 | } |
175 | ||
176 | /* Iterate all the reverse mappings of the log device. */ | |
177 | static void | |
7a2eef2b | 178 | scan_log_rmaps( |
bc94c5d6 DW |
179 | struct workqueue *wq, |
180 | xfs_agnumber_t agno, | |
181 | void *arg) | |
182 | { | |
183 | struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; | |
184 | ||
7a2eef2b | 185 | scan_dev_rmaps(ctx, agno, ctx->fsinfo.fs_logdev, arg); |
bc94c5d6 DW |
186 | } |
187 | ||
7a2eef2b DW |
188 | /* |
189 | * Scan all the blocks in a filesystem. If errors occur, this function will | |
190 | * log them and return nonzero. | |
191 | */ | |
192 | int | |
193 | scrub_scan_all_spacemaps( | |
bc94c5d6 | 194 | struct scrub_ctx *ctx, |
7a2eef2b | 195 | scrub_fsmap_iter_fn fn, |
bc94c5d6 DW |
196 | void *arg) |
197 | { | |
198 | struct workqueue wq; | |
7a2eef2b DW |
199 | struct scan_blocks sbx = { |
200 | .fn = fn, | |
201 | .arg = arg, | |
202 | }; | |
bc94c5d6 DW |
203 | xfs_agnumber_t agno; |
204 | int ret; | |
205 | ||
baed134d | 206 | ret = -workqueue_create(&wq, (struct xfs_mount *)ctx, |
bc94c5d6 DW |
207 | scrub_nproc_workqueue(ctx)); |
208 | if (ret) { | |
9d57cbfc | 209 | str_liberror(ctx, ret, _("creating fsmap workqueue")); |
7a2eef2b | 210 | return ret; |
bc94c5d6 DW |
211 | } |
212 | if (ctx->fsinfo.fs_rt) { | |
baed134d | 213 | ret = -workqueue_add(&wq, scan_rt_rmaps, |
3f9efb2e | 214 | ctx->mnt.fsgeom.agcount + 1, &sbx); |
bc94c5d6 | 215 | if (ret) { |
7a2eef2b | 216 | sbx.aborted = true; |
9d57cbfc | 217 | str_liberror(ctx, ret, _("queueing rtdev fsmap work")); |
bc94c5d6 DW |
218 | goto out; |
219 | } | |
220 | } | |
221 | if (ctx->fsinfo.fs_log) { | |
baed134d | 222 | ret = -workqueue_add(&wq, scan_log_rmaps, |
3f9efb2e | 223 | ctx->mnt.fsgeom.agcount + 2, &sbx); |
bc94c5d6 | 224 | if (ret) { |
7a2eef2b | 225 | sbx.aborted = true; |
9d57cbfc | 226 | str_liberror(ctx, ret, _("queueing logdev fsmap work")); |
bc94c5d6 DW |
227 | goto out; |
228 | } | |
229 | } | |
3f9efb2e | 230 | for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) { |
baed134d | 231 | ret = -workqueue_add(&wq, scan_ag_rmaps, agno, &sbx); |
bc94c5d6 | 232 | if (ret) { |
7a2eef2b | 233 | sbx.aborted = true; |
9d57cbfc | 234 | str_liberror(ctx, ret, _("queueing per-AG fsmap work")); |
bc94c5d6 DW |
235 | break; |
236 | } | |
237 | } | |
238 | out: | |
baed134d | 239 | ret = -workqueue_terminate(&wq); |
71296cf8 | 240 | if (ret) { |
7a2eef2b | 241 | sbx.aborted = true; |
71296cf8 DW |
242 | str_liberror(ctx, ret, _("finishing fsmap work")); |
243 | } | |
bc94c5d6 DW |
244 | workqueue_destroy(&wq); |
245 | ||
7a2eef2b DW |
246 | if (!ret && sbx.aborted) |
247 | ret = -1; | |
248 | ||
249 | return ret; | |
bc94c5d6 | 250 | } |