blob: 36210355a324ebe4f171bbf5d5df59970588ba2b [file] [log] [blame]
/*
* Copyright (C) 2018 Oracle. All Rights Reserved.
*
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "xfs.h"
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <sys/statvfs.h>
#include "workqueue.h"
#include "path.h"
#include "xfs_scrub.h"
#include "common.h"
#include "spacemap.h"
/*
* Filesystem space map iterators.
*
* Logically, we call GETFSMAP to fetch a set of space map records and
* call a function to iterate over the records. However, that's not
* what actually happens -- the work is split into separate items, with
* each AG, the realtime device, and the log device getting their own
* work items. For an XFS with a realtime device and an external log,
* this means that we can have up to ($agcount + 2) threads running at
* once.
*
* This comes into play if we want to have per-workitem memory. Maybe.
* XXX: do we really need all that ?
*/
#define FSMAP_NR 65536
/* Iterate all the fs block mappings between the two keys. */
bool
xfs_iterate_fsmap(
struct scrub_ctx *ctx,
const char *descr,
struct fsmap *keys,
xfs_fsmap_iter_fn fn,
void *arg)
{
struct fsmap_head *head;
struct fsmap *p;
bool moveon = true;
int i;
int error;
head = malloc(fsmap_sizeof(FSMAP_NR));
if (!head) {
str_errno(ctx, descr);
return false;
}
memset(head, 0, sizeof(*head));
memcpy(head->fmh_keys, keys, sizeof(struct fsmap) * 2);
head->fmh_count = FSMAP_NR;
while ((error = ioctl(ctx->mnt_fd, FS_IOC_GETFSMAP, head)) == 0) {
for (i = 0, p = head->fmh_recs;
i < head->fmh_entries;
i++, p++) {
moveon = fn(ctx, descr, p, arg);
if (!moveon)
goto out;
if (xfs_scrub_excessive_errors(ctx)) {
moveon = false;
goto out;
}
}
if (head->fmh_entries == 0)
break;
p = &head->fmh_recs[head->fmh_entries - 1];
if (p->fmr_flags & FMR_OF_LAST)
break;
fsmap_advance(head);
}
if (error) {
str_errno(ctx, descr);
moveon = false;
}
out:
free(head);
return moveon;
}
/* GETFSMAP wrappers routines. */
struct xfs_scan_blocks {
xfs_fsmap_iter_fn fn;
void *arg;
bool moveon;
};
/* Iterate all the reverse mappings of an AG. */
static void
xfs_scan_ag_blocks(
struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct xfs_scan_blocks *sbx = arg;
char descr[DESCR_BUFSZ];
struct fsmap keys[2];
off64_t bperag;
bool moveon;
bperag = (off64_t)ctx->geo.agblocks *
(off64_t)ctx->geo.blocksize;
snprintf(descr, DESCR_BUFSZ, _("dev %d:%d AG %u fsmap"),
major(ctx->fsinfo.fs_datadev),
minor(ctx->fsinfo.fs_datadev),
agno);
memset(keys, 0, sizeof(struct fsmap) * 2);
keys->fmr_device = ctx->fsinfo.fs_datadev;
keys->fmr_physical = agno * bperag;
(keys + 1)->fmr_device = ctx->fsinfo.fs_datadev;
(keys + 1)->fmr_physical = ((agno + 1) * bperag) - 1;
(keys + 1)->fmr_owner = ULLONG_MAX;
(keys + 1)->fmr_offset = ULLONG_MAX;
(keys + 1)->fmr_flags = UINT_MAX;
moveon = xfs_iterate_fsmap(ctx, descr, keys, sbx->fn, sbx->arg);
if (!moveon)
sbx->moveon = false;
}
/* Iterate all the reverse mappings of a standalone device. */
static void
xfs_scan_dev_blocks(
struct scrub_ctx *ctx,
int idx,
dev_t dev,
struct xfs_scan_blocks *sbx)
{
struct fsmap keys[2];
char descr[DESCR_BUFSZ];
bool moveon;
snprintf(descr, DESCR_BUFSZ, _("dev %d:%d fsmap"),
major(dev), minor(dev));
memset(keys, 0, sizeof(struct fsmap) * 2);
keys->fmr_device = dev;
(keys + 1)->fmr_device = dev;
(keys + 1)->fmr_physical = ULLONG_MAX;
(keys + 1)->fmr_owner = ULLONG_MAX;
(keys + 1)->fmr_offset = ULLONG_MAX;
(keys + 1)->fmr_flags = UINT_MAX;
moveon = xfs_iterate_fsmap(ctx, descr, keys, sbx->fn, sbx->arg);
if (!moveon)
sbx->moveon = false;
}
/* Iterate all the reverse mappings of the realtime device. */
static void
xfs_scan_rt_blocks(
struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
xfs_scan_dev_blocks(ctx, agno, ctx->fsinfo.fs_rtdev, arg);
}
/* Iterate all the reverse mappings of the log device. */
static void
xfs_scan_log_blocks(
struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
xfs_scan_dev_blocks(ctx, agno, ctx->fsinfo.fs_logdev, arg);
}
/* Scan all the blocks in a filesystem. */
bool
xfs_scan_all_spacemaps(
struct scrub_ctx *ctx,
xfs_fsmap_iter_fn fn,
void *arg)
{
struct workqueue wq;
struct xfs_scan_blocks sbx;
xfs_agnumber_t agno;
int ret;
sbx.moveon = true;
sbx.fn = fn;
sbx.arg = arg;
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;
}
if (ctx->fsinfo.fs_rt) {
ret = workqueue_add(&wq, xfs_scan_rt_blocks,
ctx->geo.agcount + 1, &sbx);
if (ret) {
sbx.moveon = false;
str_info(ctx, ctx->mntpoint,
_("Could not queue rtdev fsmap work."));
goto out;
}
}
if (ctx->fsinfo.fs_log) {
ret = workqueue_add(&wq, xfs_scan_log_blocks,
ctx->geo.agcount + 2, &sbx);
if (ret) {
sbx.moveon = false;
str_info(ctx, ctx->mntpoint,
_("Could not queue logdev fsmap work."));
goto out;
}
}
for (agno = 0; agno < ctx->geo.agcount; agno++) {
ret = workqueue_add(&wq, xfs_scan_ag_blocks, agno, &sbx);
if (ret) {
sbx.moveon = false;
str_info(ctx, ctx->mntpoint,
_("Could not queue AG %u fsmap work."), agno);
break;
}
}
out:
workqueue_destroy(&wq);
return sbx.moveon;
}