]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/inodes.c
libfrog: refactor open-coded bulkstat calls
[thirdparty/xfsprogs-dev.git] / scrub / inodes.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0+
372d4ba9
DW
2/*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
372d4ba9 4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
372d4ba9 5 */
a440f877 6#include "xfs.h"
372d4ba9
DW
7#include <stdint.h>
8#include <stdlib.h>
9#include <pthread.h>
10#include <sys/statvfs.h>
11#include "platform_defs.h"
372d4ba9
DW
12#include "xfs_arch.h"
13#include "xfs_format.h"
14#include "handle.h"
15#include "path.h"
16#include "workqueue.h"
17#include "xfs_scrub.h"
18#include "common.h"
19#include "inodes.h"
f31b5e12
DW
20#include "fsgeom.h"
21#include "libfrog/bulkstat.h"
372d4ba9
DW
22
23/*
24 * Iterate a range of inodes.
25 *
26 * This is a little more involved than repeatedly asking BULKSTAT for a
27 * buffer's worth of stat data for some number of inodes. We want to scan as
28 * many of the inodes that the inobt thinks there are, including the ones that
29 * are broken, but if we ask for n inodes starting at x, it'll skip the bad
30 * ones and fill from beyond the range (x + n).
31 *
32 * Therefore, we ask INUMBERS to return one inobt chunk's worth of inode
33 * bitmap information. Then we try to BULKSTAT only the inodes that were
34 * present in that chunk, and compare what we got against what INUMBERS said
35 * was there. If there's a mismatch, we know that we have an inode that fails
36 * the verifiers but we can inject the bulkstat information to force the scrub
37 * code to deal with the broken inodes.
38 *
39 * If the iteration function returns ESTALE, that means that the inode has
40 * been deleted and possibly recreated since the BULKSTAT call. We wil
41 * refresh the stat information and try again up to 30 times before reporting
42 * the staleness as an error.
43 */
44
45/*
46 * Did we get exactly the inodes we expected? If not, load them one at a
47 * time (or fake it) into the bulkstat data.
48 */
49static void
50xfs_iterate_inodes_range_check(
51 struct scrub_ctx *ctx,
52 struct xfs_inogrp *inogrp,
53 struct xfs_bstat *bstat)
54{
372d4ba9 55 struct xfs_bstat *bs;
372d4ba9
DW
56 int i;
57 int error;
58
372d4ba9
DW
59 for (i = 0, bs = bstat; i < XFS_INODES_PER_CHUNK; i++) {
60 if (!(inogrp->xi_allocmask & (1ULL << i)))
61 continue;
62 if (bs->bs_ino == inogrp->xi_startino + i) {
63 bs++;
64 continue;
65 }
66
67 /* Load the one inode. */
f31b5e12
DW
68 error = xfrog_bulkstat_single(&ctx->mnt,
69 inogrp->xi_startino + i, bs);
372d4ba9
DW
70 if (error || bs->bs_ino != inogrp->xi_startino + i) {
71 memset(bs, 0, sizeof(struct xfs_bstat));
72 bs->bs_ino = inogrp->xi_startino + i;
73 bs->bs_blksize = ctx->mnt_sv.f_frsize;
74 }
75 bs++;
76 }
77}
78
79/*
80 * Call into the filesystem for inode/bulkstat information and call our
81 * iterator function. We'll try to fill the bulkstat information in batches,
82 * but we also can detect iget failures.
83 */
84static bool
85xfs_iterate_inodes_range(
86 struct scrub_ctx *ctx,
87 const char *descr,
88 void *fshandle,
89 uint64_t first_ino,
90 uint64_t last_ino,
91 xfs_inode_iter_fn fn,
92 void *arg)
93{
3dd2705a 94 struct xfs_fsop_bulkreq igrpreq = {NULL};
372d4ba9
DW
95 struct xfs_handle handle;
96 struct xfs_inogrp inogrp;
97 struct xfs_bstat bstat[XFS_INODES_PER_CHUNK];
98 char idescr[DESCR_BUFSZ];
372d4ba9
DW
99 struct xfs_bstat *bs;
100 __u64 igrp_ino;
f31b5e12
DW
101 uint64_t ino;
102 uint32_t bulklen = 0;
372d4ba9
DW
103 __s32 igrplen = 0;
104 bool moveon = true;
105 int i;
106 int error;
107 int stale_count = 0;
108
109
110 memset(bstat, 0, XFS_INODES_PER_CHUNK * sizeof(struct xfs_bstat));
372d4ba9
DW
111
112 igrpreq.lastip = &igrp_ino;
113 igrpreq.icount = 1;
114 igrpreq.ubuffer = &inogrp;
115 igrpreq.ocount = &igrplen;
116
117 memcpy(&handle.ha_fsid, fshandle, sizeof(handle.ha_fsid));
118 handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
119 sizeof(handle.ha_fid.fid_len);
120 handle.ha_fid.fid_pad = 0;
121
122 /* Find the inode chunk & alloc mask */
123 igrp_ino = first_ino;
3f9efb2e 124 error = ioctl(ctx->mnt.fd, XFS_IOC_FSINUMBERS, &igrpreq);
372d4ba9
DW
125 while (!error && igrplen) {
126 /* Load the inodes. */
127 ino = inogrp.xi_startino - 1;
f31b5e12 128
300661d3
DW
129 /*
130 * We can have totally empty inode chunks on filesystems where
131 * there are more than 64 inodes per block. Skip these.
132 */
133 if (inogrp.xi_alloccount == 0)
134 goto igrp_retry;
f31b5e12
DW
135 error = xfrog_bulkstat(&ctx->mnt, &ino, inogrp.xi_alloccount,
136 bstat, &bulklen);
137 if (error) {
138 char errbuf[DESCR_BUFSZ];
139
140 str_info(ctx, descr, "%s", strerror_r(error,
141 errbuf, DESCR_BUFSZ));
142 }
372d4ba9
DW
143
144 xfs_iterate_inodes_range_check(ctx, &inogrp, bstat);
145
146 /* Iterate all the inodes. */
147 for (i = 0, bs = bstat; i < inogrp.xi_alloccount; i++, bs++) {
148 if (bs->bs_ino > last_ino)
149 goto out;
150
151 handle.ha_fid.fid_ino = bs->bs_ino;
152 handle.ha_fid.fid_gen = bs->bs_gen;
153 error = fn(ctx, &handle, bs, arg);
154 switch (error) {
155 case 0:
156 break;
157 case ESTALE:
158 stale_count++;
159 if (stale_count < 30) {
160 igrp_ino = inogrp.xi_startino;
161 goto igrp_retry;
162 }
163 snprintf(idescr, DESCR_BUFSZ, "inode %"PRIu64,
164 (uint64_t)bs->bs_ino);
bb5dbd06
DW
165 str_info(ctx, idescr,
166_("Changed too many times during scan; giving up."));
372d4ba9
DW
167 break;
168 case XFS_ITERATE_INODES_ABORT:
169 error = 0;
170 /* fall thru */
171 default:
172 moveon = false;
173 errno = error;
174 goto err;
175 }
176 if (xfs_scrub_excessive_errors(ctx)) {
177 moveon = false;
178 goto out;
179 }
180 }
181
182 stale_count = 0;
183igrp_retry:
3f9efb2e 184 error = ioctl(ctx->mnt.fd, XFS_IOC_FSINUMBERS, &igrpreq);
372d4ba9
DW
185 }
186
187err:
188 if (error) {
189 str_errno(ctx, descr);
190 moveon = false;
191 }
192out:
193 return moveon;
194}
195
196/* BULKSTAT wrapper routines. */
197struct xfs_scan_inodes {
198 xfs_inode_iter_fn fn;
199 void *arg;
200 bool moveon;
201};
202
203/* Scan all the inodes in an AG. */
204static void
205xfs_scan_ag_inodes(
206 struct workqueue *wq,
207 xfs_agnumber_t agno,
208 void *arg)
209{
210 struct xfs_scan_inodes *si = arg;
211 struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
212 char descr[DESCR_BUFSZ];
213 uint64_t ag_ino;
214 uint64_t next_ag_ino;
215 bool moveon;
216
217 snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u inodes"),
218 major(ctx->fsinfo.fs_datadev),
219 minor(ctx->fsinfo.fs_datadev),
220 agno);
221
a749451c
DW
222 ag_ino = cvt_agino_to_ino(&ctx->mnt, agno, 0);
223 next_ag_ino = cvt_agino_to_ino(&ctx->mnt, agno + 1, 0);
372d4ba9
DW
224
225 moveon = xfs_iterate_inodes_range(ctx, descr, ctx->fshandle, ag_ino,
226 next_ag_ino - 1, si->fn, si->arg);
227 if (!moveon)
228 si->moveon = false;
229}
230
231/* Scan all the inodes in a filesystem. */
232bool
233xfs_scan_all_inodes(
234 struct scrub_ctx *ctx,
235 xfs_inode_iter_fn fn,
236 void *arg)
237{
238 struct xfs_scan_inodes si;
239 xfs_agnumber_t agno;
240 struct workqueue wq;
241 int ret;
242
243 si.moveon = true;
244 si.fn = fn;
245 si.arg = arg;
246
247 ret = workqueue_create(&wq, (struct xfs_mount *)ctx,
248 scrub_nproc_workqueue(ctx));
249 if (ret) {
82377bde 250 str_info(ctx, ctx->mntpoint, _("Could not create workqueue."));
372d4ba9
DW
251 return false;
252 }
253
3f9efb2e 254 for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
372d4ba9
DW
255 ret = workqueue_add(&wq, xfs_scan_ag_inodes, agno, &si);
256 if (ret) {
257 si.moveon = false;
82377bde 258 str_info(ctx, ctx->mntpoint,
372d4ba9
DW
259_("Could not queue AG %u bulkstat work."), agno);
260 break;
261 }
262 }
263
264 workqueue_destroy(&wq);
265
266 return si.moveon;
267}
268
269/*
270 * Open a file by handle, or return a negative error code.
271 */
272int
273xfs_open_handle(
274 struct xfs_handle *handle)
275{
276 return open_by_fshandle(handle, sizeof(*handle),
277 O_RDONLY | O_NOATIME | O_NOFOLLOW | O_NOCTTY);
278}