Commit 48dd9117 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: implement live quotacheck inode scan

Create a new trio of scrub functions to check quota counters.  While the
dquots themselves are filesystem metadata and should be checked early,
the dquot counter values are computed from other metadata and are
therefore summary counters.  We don't plug these into the scrub dispatch
just yet, because we still need to be able to watch quota updates while
doing our scan.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 5a3ab584
......@@ -180,6 +180,7 @@ xfs-$(CONFIG_XFS_RT) += $(addprefix scrub/, \
xfs-$(CONFIG_XFS_QUOTA) += $(addprefix scrub/, \
dqiterate.o \
quota.o \
quotacheck.o \
)
# online repair
......
......@@ -710,9 +710,10 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_GQUOTA 22 /* group quotas */
#define XFS_SCRUB_TYPE_PQUOTA 23 /* project quotas */
#define XFS_SCRUB_TYPE_FSCOUNTERS 24 /* fs summary counters */
#define XFS_SCRUB_TYPE_QUOTACHECK 25 /* quota counters */
/* Number of scrub subcommands. */
#define XFS_SCRUB_TYPE_NR 25
#define XFS_SCRUB_TYPE_NR 26
/* i: Repair this metadata. */
#define XFS_SCRUB_IFLAG_REPAIR (1u << 0)
......
......@@ -29,6 +29,7 @@
#include "xfs_attr.h"
#include "xfs_reflink.h"
#include "xfs_ag.h"
#include "xfs_error.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
......@@ -82,6 +83,15 @@ __xchk_process_error(
sc->ip ? sc->ip : XFS_I(file_inode(sc->file)),
sc->sm, *error);
break;
case -ECANCELED:
/*
* ECANCELED here means that the caller set one of the scrub
* outcome flags (corrupt, xfail, xcorrupt) and wants to exit
* quickly. Set error to zero and do not continue.
*/
trace_xchk_op_error(sc, agno, bno, *error, ret_ip);
*error = 0;
break;
case -EFSBADCRC:
case -EFSCORRUPTED:
/* Note the badness but don't abort. */
......@@ -89,8 +99,7 @@ __xchk_process_error(
*error = 0;
fallthrough;
default:
trace_xchk_op_error(sc, agno, bno, *error,
ret_ip);
trace_xchk_op_error(sc, agno, bno, *error, ret_ip);
break;
}
return false;
......@@ -136,6 +145,16 @@ __xchk_fblock_process_error(
/* Used to restart an op with deadlock avoidance. */
trace_xchk_deadlock_retry(sc->ip, sc->sm, *error);
break;
case -ECANCELED:
/*
* ECANCELED here means that the caller set one of the scrub
* outcome flags (corrupt, xfail, xcorrupt) and wants to exit
* quickly. Set error to zero and do not continue.
*/
trace_xchk_file_op_error(sc, whichfork, offset, *error,
ret_ip);
*error = 0;
break;
case -EFSBADCRC:
case -EFSCORRUPTED:
/* Note the badness but don't abort. */
......@@ -227,6 +246,19 @@ xchk_block_set_corrupt(
trace_xchk_block_error(sc, xfs_buf_daddr(bp), __return_address);
}
#ifdef CONFIG_XFS_QUOTA
/* Record a corrupt quota counter. */
void
xchk_qcheck_set_corrupt(
struct xfs_scrub *sc,
unsigned int dqtype,
xfs_dqid_t id)
{
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
trace_xchk_qcheck_error(sc, dqtype, id, __return_address);
}
#endif
/* Record a corruption while cross-referencing. */
void
xchk_block_xref_set_corrupt(
......
......@@ -55,6 +55,10 @@ void xchk_block_set_corrupt(struct xfs_scrub *sc,
void xchk_ino_set_corrupt(struct xfs_scrub *sc, xfs_ino_t ino);
void xchk_fblock_set_corrupt(struct xfs_scrub *sc, int whichfork,
xfs_fileoff_t offset);
#ifdef CONFIG_XFS_QUOTA
void xchk_qcheck_set_corrupt(struct xfs_scrub *sc, unsigned int dqtype,
xfs_dqid_t id);
#endif
void xchk_block_xref_set_corrupt(struct xfs_scrub *sc,
struct xfs_buf *bp);
......@@ -106,6 +110,7 @@ xchk_setup_rtsummary(struct xfs_scrub *sc)
#ifdef CONFIG_XFS_QUOTA
int xchk_ino_dqattach(struct xfs_scrub *sc);
int xchk_setup_quota(struct xfs_scrub *sc);
int xchk_setup_quotacheck(struct xfs_scrub *sc);
#else
static inline int
xchk_ino_dqattach(struct xfs_scrub *sc)
......@@ -117,6 +122,11 @@ xchk_setup_quota(struct xfs_scrub *sc)
{
return -ENOENT;
}
static inline int
xchk_setup_quotacheck(struct xfs_scrub *sc)
{
return -ENOENT;
}
#endif
int xchk_setup_fscounters(struct xfs_scrub *sc);
......
......@@ -105,6 +105,7 @@ static const struct xchk_health_map type_to_health_flag[XFS_SCRUB_TYPE_NR] = {
[XFS_SCRUB_TYPE_GQUOTA] = { XHG_FS, XFS_SICK_FS_GQUOTA },
[XFS_SCRUB_TYPE_PQUOTA] = { XHG_FS, XFS_SICK_FS_PQUOTA },
[XFS_SCRUB_TYPE_FSCOUNTERS] = { XHG_FS, XFS_SICK_FS_COUNTERS },
[XFS_SCRUB_TYPE_QUOTACHECK] = { XHG_FS, XFS_SICK_FS_QUOTACHECK },
};
/* Return the health status mask for this scrub type. */
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2020-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_QUOTACHECK_H__
#define __XFS_SCRUB_QUOTACHECK_H__
/* Quota counters for live quotacheck. */
struct xqcheck_dquot {
/* block usage count */
int64_t bcount;
/* inode usage count */
int64_t icount;
/* realtime block usage count */
int64_t rtbcount;
/* Record state */
unsigned int flags;
};
/*
* This incore dquot record has been written at least once. We never want to
* store an xqcheck_dquot that looks uninitialized.
*/
#define XQCHECK_DQUOT_WRITTEN (1U << 0)
/* Already checked this dquot. */
#define XQCHECK_DQUOT_COMPARE_SCANNED (1U << 1)
/* Live quotacheck control structure. */
struct xqcheck {
struct xfs_scrub *sc;
/* Shadow dquot counter data. */
struct xfarray *ucounts;
struct xfarray *gcounts;
struct xfarray *pcounts;
/* Lock protecting quotacheck count observations */
struct mutex lock;
struct xchk_iscan iscan;
};
/* Return the incore counter array for a given quota type. */
static inline struct xfarray *
xqcheck_counters_for(
struct xqcheck *xqc,
xfs_dqtype_t dqtype)
{
switch (dqtype) {
case XFS_DQTYPE_USER:
return xqc->ucounts;
case XFS_DQTYPE_GROUP:
return xqc->gcounts;
case XFS_DQTYPE_PROJ:
return xqc->pcounts;
}
ASSERT(0);
return NULL;
}
#endif /* __XFS_SCRUB_QUOTACHECK_H__ */
......@@ -360,6 +360,12 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.scrub = xchk_fscounters,
.repair = xrep_notsupported,
},
[XFS_SCRUB_TYPE_QUOTACHECK] = { /* quota counters */
.type = ST_FS,
.setup = xchk_setup_quotacheck,
.scrub = xchk_quotacheck,
.repair = xrep_notsupported,
},
};
static int
......
......@@ -167,12 +167,18 @@ xchk_rtsummary(struct xfs_scrub *sc)
#endif
#ifdef CONFIG_XFS_QUOTA
int xchk_quota(struct xfs_scrub *sc);
int xchk_quotacheck(struct xfs_scrub *sc);
#else
static inline int
xchk_quota(struct xfs_scrub *sc)
{
return -ENOENT;
}
static inline int
xchk_quotacheck(struct xfs_scrub *sc)
{
return -ENOENT;
}
#endif
int xchk_fscounters(struct xfs_scrub *sc);
......
......@@ -77,6 +77,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = {
[XFS_SCRUB_TYPE_GQUOTA] = "grpquota",
[XFS_SCRUB_TYPE_PQUOTA] = "prjquota",
[XFS_SCRUB_TYPE_FSCOUNTERS] = "fscounters",
[XFS_SCRUB_TYPE_QUOTACHECK] = "quotacheck",
};
/* Format the scrub stats into a text buffer, similar to pcp style. */
......
......@@ -15,6 +15,7 @@
#include <linux/tracepoint.h>
#include "xfs_bit.h"
#include "xfs_quota_defs.h"
struct xfs_scrub;
struct xfile;
......@@ -65,6 +66,7 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_UQUOTA);
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_GQUOTA);
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_PQUOTA);
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_FSCOUNTERS);
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_QUOTACHECK);
#define XFS_SCRUB_TYPE_STRINGS \
{ XFS_SCRUB_TYPE_PROBE, "probe" }, \
......@@ -91,7 +93,8 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_FSCOUNTERS);
{ XFS_SCRUB_TYPE_UQUOTA, "usrquota" }, \
{ XFS_SCRUB_TYPE_GQUOTA, "grpquota" }, \
{ XFS_SCRUB_TYPE_PQUOTA, "prjquota" }, \
{ XFS_SCRUB_TYPE_FSCOUNTERS, "fscounters" }
{ XFS_SCRUB_TYPE_FSCOUNTERS, "fscounters" }, \
{ XFS_SCRUB_TYPE_QUOTACHECK, "quotacheck" }
#define XFS_SCRUB_FLAG_STRINGS \
{ XFS_SCRUB_IFLAG_REPAIR, "repair" }, \
......@@ -397,6 +400,29 @@ DEFINE_SCRUB_DQITER_EVENT(xchk_dquot_iter_revalidate_bmap);
DEFINE_SCRUB_DQITER_EVENT(xchk_dquot_iter_advance_bmap);
DEFINE_SCRUB_DQITER_EVENT(xchk_dquot_iter_advance_incore);
DEFINE_SCRUB_DQITER_EVENT(xchk_dquot_iter);
TRACE_EVENT(xchk_qcheck_error,
TP_PROTO(struct xfs_scrub *sc, xfs_dqtype_t dqtype, xfs_dqid_t id,
void *ret_ip),
TP_ARGS(sc, dqtype, id, ret_ip),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_dqtype_t, dqtype)
__field(xfs_dqid_t, id)
__field(void *, ret_ip)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->dqtype = dqtype;
__entry->id = id;
__entry->ret_ip = ret_ip;
),
TP_printk("dev %d:%d dquot type %s id 0x%x ret_ip %pS",
MAJOR(__entry->dev), MINOR(__entry->dev),
__print_symbolic(__entry->dqtype, XFS_DQTYPE_STRINGS),
__entry->id,
__entry->ret_ip)
);
#endif /* CONFIG_XFS_QUOTA */
TRACE_EVENT(xchk_incomplete,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment