]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/spacemap.c
libfrog: convert workqueue.c functions to negative error codes
[thirdparty/xfsprogs-dev.git] / scrub / spacemap.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 */
6 #include "xfs.h"
7 #include <stdint.h>
8 #include <string.h>
9 #include <pthread.h>
10 #include <sys/statvfs.h>
11 #include "libfrog/workqueue.h"
12 #include "libfrog/paths.h"
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 /*
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(
40 struct scrub_ctx *ctx,
41 struct fsmap *keys,
42 scrub_fsmap_iter_fn fn,
43 void *arg)
44 {
45 struct fsmap_head *head;
46 struct fsmap *p;
47 int i;
48 int error;
49
50 head = malloc(fsmap_sizeof(FSMAP_NR));
51 if (!head)
52 return errno;
53
54 memset(head, 0, sizeof(*head));
55 memcpy(head->fmh_keys, keys, sizeof(struct fsmap) * 2);
56 head->fmh_count = FSMAP_NR;
57
58 while ((error = ioctl(ctx->mnt.fd, FS_IOC_GETFSMAP, head)) == 0) {
59 for (i = 0, p = head->fmh_recs;
60 i < head->fmh_entries;
61 i++, p++) {
62 error = fn(ctx, p, arg);
63 if (error)
64 goto out;
65 if (xfs_scrub_excessive_errors(ctx))
66 goto out;
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 }
76 if (error)
77 error = errno;
78 out:
79 free(head);
80 return error;
81 }
82
83 /* GETFSMAP wrappers routines. */
84 struct scan_blocks {
85 scrub_fsmap_iter_fn fn;
86 void *arg;
87 bool aborted;
88 };
89
90 /* Iterate all the reverse mappings of an AG. */
91 static void
92 scan_ag_rmaps(
93 struct workqueue *wq,
94 xfs_agnumber_t agno,
95 void *arg)
96 {
97 struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
98 struct scan_blocks *sbx = arg;
99 struct fsmap keys[2];
100 off64_t bperag;
101 int ret;
102
103 bperag = (off64_t)ctx->mnt.fsgeom.agblocks *
104 (off64_t)ctx->mnt.fsgeom.blocksize;
105
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
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 }
129 }
130
131 /* Iterate all the reverse mappings of a standalone device. */
132 static void
133 scan_dev_rmaps(
134 struct scrub_ctx *ctx,
135 int idx,
136 dev_t dev,
137 struct scan_blocks *sbx)
138 {
139 struct fsmap keys[2];
140 int ret;
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
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 }
162 }
163
164 /* Iterate all the reverse mappings of the realtime device. */
165 static void
166 scan_rt_rmaps(
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
173 scan_dev_rmaps(ctx, agno, ctx->fsinfo.fs_rtdev, arg);
174 }
175
176 /* Iterate all the reverse mappings of the log device. */
177 static void
178 scan_log_rmaps(
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
185 scan_dev_rmaps(ctx, agno, ctx->fsinfo.fs_logdev, arg);
186 }
187
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(
194 struct scrub_ctx *ctx,
195 scrub_fsmap_iter_fn fn,
196 void *arg)
197 {
198 struct workqueue wq;
199 struct scan_blocks sbx = {
200 .fn = fn,
201 .arg = arg,
202 };
203 xfs_agnumber_t agno;
204 int ret;
205
206 ret = -workqueue_create(&wq, (struct xfs_mount *)ctx,
207 scrub_nproc_workqueue(ctx));
208 if (ret) {
209 str_liberror(ctx, ret, _("creating fsmap workqueue"));
210 return ret;
211 }
212 if (ctx->fsinfo.fs_rt) {
213 ret = -workqueue_add(&wq, scan_rt_rmaps,
214 ctx->mnt.fsgeom.agcount + 1, &sbx);
215 if (ret) {
216 sbx.aborted = true;
217 str_liberror(ctx, ret, _("queueing rtdev fsmap work"));
218 goto out;
219 }
220 }
221 if (ctx->fsinfo.fs_log) {
222 ret = -workqueue_add(&wq, scan_log_rmaps,
223 ctx->mnt.fsgeom.agcount + 2, &sbx);
224 if (ret) {
225 sbx.aborted = true;
226 str_liberror(ctx, ret, _("queueing logdev fsmap work"));
227 goto out;
228 }
229 }
230 for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
231 ret = -workqueue_add(&wq, scan_ag_rmaps, agno, &sbx);
232 if (ret) {
233 sbx.aborted = true;
234 str_liberror(ctx, ret, _("queueing per-AG fsmap work"));
235 break;
236 }
237 }
238 out:
239 ret = -workqueue_terminate(&wq);
240 if (ret) {
241 sbx.aborted = true;
242 str_liberror(ctx, ret, _("finishing fsmap work"));
243 }
244 workqueue_destroy(&wq);
245
246 if (!ret && sbx.aborted)
247 ret = -1;
248
249 return ret;
250 }