#include "xfs_arch.h"
#include "xfs_format.h"
#include "handle.h"
-#include "path.h"
-#include "workqueue.h"
+#include "libfrog/paths.h"
+#include "libfrog/workqueue.h"
#include "xfs_scrub.h"
#include "common.h"
#include "inodes.h"
+#include "libfrog/fsgeom.h"
+#include "libfrog/bulkstat.h"
/*
* Iterate a range of inodes.
*/
/*
- * Did we get exactly the inodes we expected? If not, load them one at a
- * time (or fake it) into the bulkstat data.
+ * Run bulkstat on an entire inode allocation group, then check that we got
+ * exactly the inodes we expected. If not, load them one at a time (or fake
+ * it) into the bulkstat data.
*/
static void
-xfs_iterate_inodes_range_check(
+bulkstat_for_inumbers(
struct scrub_ctx *ctx,
- struct xfs_inogrp *inogrp,
- struct xfs_bstat *bstat)
+ const char *descr,
+ const struct xfs_inumbers *inumbers,
+ struct xfs_bulkstat_req *breq)
{
- struct xfs_fsop_bulkreq onereq = {0};
- struct xfs_bstat *bs;
- __u64 oneino;
- __s32 onelen = 0;
+ struct xfs_bulkstat *bstat = breq->bulkstat;
+ struct xfs_bulkstat *bs;
int i;
int error;
- onereq.lastip = &oneino;
- onereq.icount = 1;
- onereq.ocount = &onelen;
+ /* First we try regular bulkstat, for speed. */
+ breq->hdr.ino = inumbers->xi_startino;
+ breq->hdr.icount = inumbers->xi_alloccount;
+ error = -xfrog_bulkstat(&ctx->mnt, breq);
+ if (error) {
+ char errbuf[DESCR_BUFSZ];
+
+ str_info(ctx, descr, "%s",
+ strerror_r(error, errbuf, DESCR_BUFSZ));
+ }
+ /*
+ * Check each of the stats we got back to make sure we got the inodes
+ * we asked for.
+ */
for (i = 0, bs = bstat; i < XFS_INODES_PER_CHUNK; i++) {
- if (!(inogrp->xi_allocmask & (1ULL << i)))
+ if (!(inumbers->xi_allocmask & (1ULL << i)))
continue;
- if (bs->bs_ino == inogrp->xi_startino + i) {
+ if (bs->bs_ino == inumbers->xi_startino + i) {
bs++;
continue;
}
/* Load the one inode. */
- oneino = inogrp->xi_startino + i;
- onereq.ubuffer = bs;
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSBULKSTAT_SINGLE,
- &onereq);
- if (error || bs->bs_ino != inogrp->xi_startino + i) {
- memset(bs, 0, sizeof(struct xfs_bstat));
- bs->bs_ino = inogrp->xi_startino + i;
+ error = -xfrog_bulkstat_single(&ctx->mnt,
+ inumbers->xi_startino + i, 0, bs);
+ if (error || bs->bs_ino != inumbers->xi_startino + i) {
+ memset(bs, 0, sizeof(struct xfs_bulkstat));
+ bs->bs_ino = inumbers->xi_startino + i;
bs->bs_blksize = ctx->mnt_sv.f_frsize;
}
bs++;
}
}
+/* BULKSTAT wrapper routines. */
+struct scan_inodes {
+ scrub_inode_iter_fn fn;
+ void *arg;
+ bool aborted;
+};
+
/*
* Call into the filesystem for inode/bulkstat information and call our
* iterator function. We'll try to fill the bulkstat information in batches,
* but we also can detect iget failures.
*/
-static bool
-xfs_iterate_inodes_range(
- struct scrub_ctx *ctx,
- const char *descr,
- void *fshandle,
- uint64_t first_ino,
- uint64_t last_ino,
- xfs_inode_iter_fn fn,
+static void
+scan_ag_inodes(
+ struct workqueue *wq,
+ xfs_agnumber_t agno,
void *arg)
{
- struct xfs_fsop_bulkreq igrpreq = {0};
- struct xfs_fsop_bulkreq bulkreq = {0};
struct xfs_handle handle;
- struct xfs_inogrp inogrp;
- struct xfs_bstat bstat[XFS_INODES_PER_CHUNK];
- char idescr[DESCR_BUFSZ];
- char buf[DESCR_BUFSZ];
- struct xfs_bstat *bs;
- __u64 igrp_ino;
- __u64 ino;
- __s32 bulklen = 0;
- __s32 igrplen = 0;
- bool moveon = true;
+ char descr[DESCR_BUFSZ];
+ struct xfs_inumbers_req *ireq;
+ struct xfs_bulkstat_req *breq;
+ struct scan_inodes *si = arg;
+ struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
+ struct xfs_bulkstat *bs;
+ struct xfs_inumbers *inumbers;
int i;
int error;
int stale_count = 0;
+ snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u inodes"),
+ major(ctx->fsinfo.fs_datadev),
+ minor(ctx->fsinfo.fs_datadev),
+ agno);
- memset(bstat, 0, XFS_INODES_PER_CHUNK * sizeof(struct xfs_bstat));
- bulkreq.lastip = &ino;
- bulkreq.icount = XFS_INODES_PER_CHUNK;
- bulkreq.ubuffer = &bstat;
- bulkreq.ocount = &bulklen;
-
- igrpreq.lastip = &igrp_ino;
- igrpreq.icount = 1;
- igrpreq.ubuffer = &inogrp;
- igrpreq.ocount = &igrplen;
-
- memcpy(&handle.ha_fsid, fshandle, sizeof(handle.ha_fsid));
+ memcpy(&handle.ha_fsid, ctx->fshandle, sizeof(handle.ha_fsid));
handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
sizeof(handle.ha_fid.fid_len);
handle.ha_fid.fid_pad = 0;
+ error = -xfrog_bulkstat_alloc_req(XFS_INODES_PER_CHUNK, 0, &breq);
+ if (error) {
+ str_liberror(ctx, error, descr);
+ si->aborted = true;
+ return;
+ }
+
+ error = -xfrog_inumbers_alloc_req(1, 0, &ireq);
+ if (error) {
+ str_liberror(ctx, error, descr);
+ free(breq);
+ si->aborted = true;
+ return;
+ }
+ inumbers = &ireq->inumbers[0];
+ xfrog_inumbers_set_ag(ireq, agno);
+
/* Find the inode chunk & alloc mask */
- igrp_ino = first_ino;
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSINUMBERS, &igrpreq);
- while (!error && igrplen) {
- /* Load the inodes. */
- ino = inogrp.xi_startino - 1;
- bulkreq.icount = inogrp.xi_alloccount;
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSBULKSTAT, &bulkreq);
- if (error)
- str_info(ctx, descr, "%s", strerror_r(errno,
- buf, DESCR_BUFSZ));
+ error = -xfrog_inumbers(&ctx->mnt, ireq);
+ while (!error && !si->aborted && ireq->hdr.ocount > 0) {
+ /*
+ * We can have totally empty inode chunks on filesystems where
+ * there are more than 64 inodes per block. Skip these.
+ */
+ if (inumbers->xi_alloccount == 0)
+ goto igrp_retry;
- xfs_iterate_inodes_range_check(ctx, &inogrp, bstat);
+ bulkstat_for_inumbers(ctx, descr, inumbers, breq);
/* Iterate all the inodes. */
- for (i = 0, bs = bstat; i < inogrp.xi_alloccount; i++, bs++) {
- if (bs->bs_ino > last_ino)
- goto out;
-
+ for (i = 0, bs = breq->bulkstat;
+ !si->aborted && i < inumbers->xi_alloccount;
+ i++, bs++) {
handle.ha_fid.fid_ino = bs->bs_ino;
handle.ha_fid.fid_gen = bs->bs_gen;
- error = fn(ctx, &handle, bs, arg);
+ error = si->fn(ctx, &handle, bs, si->arg);
switch (error) {
case 0:
break;
- case ESTALE:
+ case ESTALE: {
+ char idescr[DESCR_BUFSZ];
+
stale_count++;
if (stale_count < 30) {
- igrp_ino = inogrp.xi_startino;
+ ireq->hdr.ino = inumbers->xi_startino;
goto igrp_retry;
}
- snprintf(idescr, DESCR_BUFSZ, "inode %"PRIu64,
- (uint64_t)bs->bs_ino);
+ scrub_render_ino_descr(ctx, idescr, DESCR_BUFSZ,
+ bs->bs_ino, bs->bs_gen, NULL);
str_info(ctx, idescr,
_("Changed too many times during scan; giving up."));
break;
- case XFS_ITERATE_INODES_ABORT:
+ }
+ case ECANCELED:
error = 0;
/* fall thru */
default:
- moveon = false;
- errno = error;
goto err;
}
if (xfs_scrub_excessive_errors(ctx)) {
- moveon = false;
+ si->aborted = true;
goto out;
}
}
stale_count = 0;
igrp_retry:
- error = ioctl(ctx->mnt_fd, XFS_IOC_FSINUMBERS, &igrpreq);
+ error = -xfrog_inumbers(&ctx->mnt, ireq);
}
err:
if (error) {
- str_errno(ctx, descr);
- moveon = false;
+ str_liberror(ctx, error, descr);
+ si->aborted = true;
}
out:
- return moveon;
+ free(ireq);
+ free(breq);
}
-/* BULKSTAT wrapper routines. */
-struct xfs_scan_inodes {
- xfs_inode_iter_fn fn;
- void *arg;
- bool moveon;
-};
-
-/* Scan all the inodes in an AG. */
-static void
-xfs_scan_ag_inodes(
- struct workqueue *wq,
- xfs_agnumber_t agno,
- void *arg)
-{
- struct xfs_scan_inodes *si = arg;
- struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
- char descr[DESCR_BUFSZ];
- uint64_t ag_ino;
- uint64_t next_ag_ino;
- bool moveon;
-
- snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u inodes"),
- major(ctx->fsinfo.fs_datadev),
- minor(ctx->fsinfo.fs_datadev),
- agno);
-
- ag_ino = (__u64)agno << (ctx->inopblog + ctx->agblklog);
- next_ag_ino = (__u64)(agno + 1) << (ctx->inopblog + ctx->agblklog);
-
- moveon = xfs_iterate_inodes_range(ctx, descr, ctx->fshandle, ag_ino,
- next_ag_ino - 1, si->fn, si->arg);
- if (!moveon)
- si->moveon = false;
-}
-
-/* Scan all the inodes in a filesystem. */
-bool
-xfs_scan_all_inodes(
+/*
+ * Scan all the inodes in a filesystem. On error, this function will log
+ * an error message and return -1.
+ */
+int
+scrub_scan_all_inodes(
struct scrub_ctx *ctx,
- xfs_inode_iter_fn fn,
+ scrub_inode_iter_fn fn,
void *arg)
{
- struct xfs_scan_inodes si;
+ struct scan_inodes si = {
+ .fn = fn,
+ .arg = arg,
+ };
xfs_agnumber_t agno;
struct workqueue wq;
int ret;
- si.moveon = true;
- si.fn = fn;
- si.arg = arg;
-
- ret = workqueue_create(&wq, (struct xfs_mount *)ctx,
+ ret = -workqueue_create(&wq, (struct xfs_mount *)ctx,
scrub_nproc_workqueue(ctx));
if (ret) {
- str_info(ctx, ctx->mntpoint, _("Could not create workqueue."));
- return false;
+ str_liberror(ctx, ret, _("creating bulkstat workqueue"));
+ return -1;
}
- for (agno = 0; agno < ctx->geo.agcount; agno++) {
- ret = workqueue_add(&wq, xfs_scan_ag_inodes, agno, &si);
+ for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
+ ret = -workqueue_add(&wq, scan_ag_inodes, agno, &si);
if (ret) {
- si.moveon = false;
- str_info(ctx, ctx->mntpoint,
-_("Could not queue AG %u bulkstat work."), agno);
+ si.aborted = true;
+ str_liberror(ctx, ret, _("queueing bulkstat work"));
break;
}
}
+ ret = -workqueue_terminate(&wq);
+ if (ret) {
+ si.aborted = true;
+ str_liberror(ctx, ret, _("finishing bulkstat work"));
+ }
workqueue_destroy(&wq);
- return si.moveon;
+ return si.aborted ? -1 : 0;
}
-/*
- * Open a file by handle, or return a negative error code.
- */
+/* Open a file by handle, returning either the fd or -1 on error. */
int
-xfs_open_handle(
+scrub_open_handle(
struct xfs_handle *handle)
{
return open_by_fshandle(handle, sizeof(*handle),