From patchwork Sun Dec 31 23:19:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508080 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9AA14C154 for ; Sun, 31 Dec 2023 23:19:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BpJwzlFk" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0A507C433C9; Sun, 31 Dec 2023 23:19:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064798; bh=HNd0bgulWfNjZJSWfh3MGvjffDN9Ozyp0WRp9kIV1Ko=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=BpJwzlFkGbzIlKiQoXW/hNbL4hZ8F+AzO2Ml8kz4fMz1mKnqFpatDSAm4fBRjgG5B a9hkNFK/P0yNTVjWDOFUacZEmYM3Aw5UyvexLXyB0hpYSiGw1JcaSZeVgFGkfIjj22 2GHGvV9emAIs6XILTxpBOvuzVzIIY2uawD06pR7LX3wPvq4MJUu1sDv4TagDL+1+4R p4IrLx06nSDAs35PqREBtVJQUPN/stN/yMab4MA6WuJUHAAbviIoxMAF62JEnpiVEr 0qEXGQKCEhn0eESumy8KleidZ+afBS+51rCpOTb/SrZeDGi+xAF3M+fqIPvRdtYLWA JtMYAFONFA4aA== Date: Sun, 31 Dec 2023 15:19:57 -0800 Subject: [PATCH 01/10] xfs: introduce vectored scrub mode From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007856.1806194.7426360300744176144.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Introduce a variant on XFS_SCRUB_METADATA that allows for vectored mode. Signed-off-by: Darrick J. Wong --- libxfs/xfs_fs.h | 10 ++ libxfs/xfs_fs_staging.h | 32 ++++++ man/man2/ioctl_xfs_scrubv_metadata.2 | 168 ++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 man/man2/ioctl_xfs_scrubv_metadata.2 diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h index 2499a20f5f7..77fbca573e1 100644 --- a/libxfs/xfs_fs.h +++ b/libxfs/xfs_fs.h @@ -725,6 +725,15 @@ struct xfs_scrub_metadata { /* Number of scrub subcommands. */ #define XFS_SCRUB_TYPE_NR 29 +/* + * This special type code only applies to the vectored scrub implementation. + * + * If any of the previous scrub vectors recorded runtime errors or have + * sv_flags bits set that match the OFLAG bits in the barrier vector's + * sv_flags, set the barrier's sv_ret to -ECANCELED and return to userspace. + */ +#define XFS_SCRUB_TYPE_BARRIER (-1U) + /* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1u << 0) @@ -813,6 +822,7 @@ struct xfs_scrub_metadata { #define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks) /* XFS_IOC_GETFSMAP ------ hoisted 59 */ #define XFS_IOC_SCRUB_METADATA _IOWR('X', 60, struct xfs_scrub_metadata) +/* XFS_IOC_SCRUBV_METADATA -- staging 60 */ #define XFS_IOC_AG_GEOMETRY _IOWR('X', 61, struct xfs_ag_geometry) /* XFS_IOC_GETPARENTS ---- staging 62 */ diff --git a/libxfs/xfs_fs_staging.h b/libxfs/xfs_fs_staging.h index e0650af0558..69d29f213af 100644 --- a/libxfs/xfs_fs_staging.h +++ b/libxfs/xfs_fs_staging.h @@ -170,4 +170,36 @@ xfs_getparents_rec( #define XFS_IOC_GETPARENTS _IOWR('X', 62, struct xfs_getparents) +/* Vectored scrub calls to reduce the number of kernel transitions. */ + +struct xfs_scrub_vec { + __u32 sv_type; /* XFS_SCRUB_TYPE_* */ + __u32 sv_flags; /* XFS_SCRUB_FLAGS_* */ + __s32 sv_ret; /* 0 or a negative error code */ + __u32 sv_reserved; /* must be zero */ +}; + +/* Vectored metadata scrub control structure. */ +struct xfs_scrub_vec_head { + __u64 svh_ino; /* inode number. */ + __u32 svh_gen; /* inode generation. */ + __u32 svh_agno; /* ag number. */ + __u32 svh_flags; /* XFS_SCRUB_VEC_FLAGS_* */ + __u16 svh_rest_us; /* wait this much time between vector items */ + __u16 svh_nr; /* number of svh_vecs */ + __u64 svh_reserved; /* must be zero */ + + struct xfs_scrub_vec svh_vecs[]; +}; + +#define XFS_SCRUB_VEC_FLAGS_ALL (0) + +static inline size_t sizeof_xfs_scrub_vec(unsigned int nr) +{ + return sizeof(struct xfs_scrub_vec_head) + + nr * sizeof(struct xfs_scrub_vec); +} + +#define XFS_IOC_SCRUBV_METADATA _IOWR('X', 60, struct xfs_scrub_vec_head) + #endif /* __XFS_FS_STAGING_H__ */ diff --git a/man/man2/ioctl_xfs_scrubv_metadata.2 b/man/man2/ioctl_xfs_scrubv_metadata.2 new file mode 100644 index 00000000000..05a4adaba48 --- /dev/null +++ b/man/man2/ioctl_xfs_scrubv_metadata.2 @@ -0,0 +1,168 @@ +.\" Copyright (c) 2023-2024 Oracle. All rights reserved. +.\" +.\" %%%LICENSE_START(GPLv2+_DOC_FULL) +.\" SPDX-License-Identifier: GPL-2.0-or-later +.\" %%%LICENSE_END +.TH IOCTL-XFS-SCRUBV-METADATA 2 2023-08-18 "XFS" +.SH NAME +ioctl_xfs_scrubv_metadata \- check a lot of XFS filesystem metadata +.SH SYNOPSIS +.br +.B #include +.br +.B #include +.PP +.BI "int ioctl(int " dest_fd ", XFS_IOC_SCRUBV_METADATA, struct xfs_scrub_vec_head *" arg ); +.SH DESCRIPTION +This XFS ioctl asks the kernel driver to examine several pieces of filesystem +metadata for errors or suboptimal metadata. +Multiple scrub types can be invoked to target a single filesystem object. +See +.BR ioctl_xfs_scrub_metadata (2) +for a discussion of metadata validation, and documentation of the various +.B XFS_SCRUB_TYPE +and +.B XFS_SCRUB_FLAGS +values referenced below. + +The types and location of the metadata to scrub are conveyed as a vector with +a header of the following form: +.PP +.in +4n +.nf + +struct xfs_scrub_vec_head { + __u64 svh_ino; + __u32 svh_gen; + __u32 svh_agno; + __u32 svh_flags; + __u16 svh_rest_us; + __u16 svh_nr; + __u64 svh_reserved; + + struct xfs_scrub_vec svh_vecs[]; +}; +.fi +.in +.PP +The field +.IR svh_ino , +.IR svh_gen , +and +.IR svh_agno +correspond to the +.IR sm_ino , +.IR sm_gen , +and +.IR sm_agno +fields of the regular scrub ioctl. +Exactly one filesystem object can be specified in a single call. +The kernel will proceed with each vector in +.I svh_vecs +until progress is no longer possible. + +The field +.I svh_rest_us +specifies an amount of time to pause between each scrub invocation to give +the system a chance to process other requests. + +The field +.I svh_nr +specifies the number of vectors in the +.I svh_recs +flex array. + +.PP +The field +.I svh_reserved +must be zero. + +Each vector has the following form: +.PP +.in +4n +.nf + +struct xfs_scrub_vec { + __u32 sv_type; + __u32 sv_flags; + __s32 sv_ret; + __u32 sv_reserved; +}; +.fi +.in + +.PP +The fields +.I sv_type +and +.I sv_flags +indicate the type of metadata to check and the behavioral changes that +userspace will permit of the kernel. +The +.I sv_flags +field will be updated upon completion of the scrub call. +See the documentation of +.B XFS_SCRUB_TYPE_* +and +.B XFS_SCRUB_[IO]FLAG_* +values in +.BR ioctl_xfs_scrub_metadata (2) +for a detailed description of their purpose. + +.PP +If a vector's +.I sv_type +field is set to the value +.BR XFS_SCRUB_TYPE_BARRIER , +the kernel will stop processing vectors and return to userspace if a scrubber +flags corruption by setting one of the +.B XFS_SCRUB_OFLAG_* +values in +.I sv_flags +or +returns an operation error in +.IR sv_ret . +Otherwise, the kernel returns only after processing all vectors. + +The +.I sv_ret +field is set to the return value of the scrub function. +See the RETURN VALUE +section of the +.BR ioctl_xfs_scrub_metadata (2) +manual page for more information. + +The +.B sv_reserved +field must be zero. + +.SH RETURN VALUE +On error, \-1 is returned, and +.I errno +is set to indicate the error. +.PP +.SH ERRORS +Error codes can be one of, but are not limited to, the following: +.TP +.B EINVAL +One or more of the arguments specified is invalid. +.TP +.B EINTR +The operation was interrupted. +.TP +.B ENOMEM +There was not sufficient memory to perform the scrub or repair operation. +.TP +.B EFAULT +A memory fault was encountered while reading or writing the vector. +.SH CONFORMING TO +This API is specific to XFS filesystem on the Linux kernel. +.SH NOTES +These operations may block other filesystem operations for a long time. +A calling process can stop the operation by being sent a fatal +signal, but non-fatal signals are blocked. +.SH SEE ALSO +.BR ioctl (2) +.BR ioctl_xfs_scrub_metadata (2) +.BR xfs_scrub (8) +.BR xfs_repair (8) From patchwork Sun Dec 31 23:20:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508081 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1FC45C2CC for ; Sun, 31 Dec 2023 23:20:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="f/0gXBN3" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 986DBC433C8; Sun, 31 Dec 2023 23:20:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064813; bh=rLEWCoTbexiKW/OGN75uYpHafbeupx1GvUJVdpvp63o=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=f/0gXBN3gv2ppz0AsOsiyaLSwvi8IQbLzhnoY6aBS/Xa+l7HHge0jvONdFkKP5yth 9dGAJvdzeFiKImmYcl90QmdcP+qgpncXSLxUVTC45nGyZ05p7YGkksvJ+b0V1tNksJ 130gtSMC6IeF/+cH/ctmUBA8hH4m/lsNW9Jh3dM4jI3+npxGF8zwa4TAbjiP36MV95 K+1rNbV+34BB1evRgCOp3GFOzG82BoE5oi7vk78O9FdDR/azYjAsNYAgJG3YJ5N2+P 0CovVXjiT3YKgCeT9v+BhBMUBX6keiOkNSmxNy+CsC3llcOa64S6Y7TLFvxSoD1s6o F0XQ06YoPUQAA== Date: Sun, 31 Dec 2023 15:20:13 -0800 Subject: [PATCH 02/10] libfrog: support vectored scrub From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007870.1806194.6377941671214699137.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Enhance libfrog to support performing vectored metadata scrub. Signed-off-by: Darrick J. Wong --- libfrog/fsgeom.h | 6 +++ libfrog/scrub.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ libfrog/scrub.h | 4 ++ 3 files changed, 134 insertions(+) diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h index 7e002c5137a..4f3542eafec 100644 --- a/libfrog/fsgeom.h +++ b/libfrog/fsgeom.h @@ -56,6 +56,12 @@ struct xfs_fd { /* Only use XFS_IOC_EXCHANGE_RANGE for file data exchanges. */ #define XFROG_FLAG_FORCE_EXCH_RANGE (1 << 3) +/* Only use the older one-at-a-time scrub ioctl. */ +#define XFROG_FLAG_SCRUB_FORCE_SINGLE (1 << 4) + +/* Only use the vectored scrub ioctl. */ +#define XFROG_FLAG_SCRUB_FORCE_VECTOR (1 << 5) + /* Static initializers */ #define XFS_FD_INIT(_fd) { .fd = (_fd), } #define XFS_FD_INIT_EMPTY XFS_FD_INIT(-1) diff --git a/libfrog/scrub.c b/libfrog/scrub.c index a2146e228f5..8264aab00ef 100644 --- a/libfrog/scrub.c +++ b/libfrog/scrub.c @@ -171,3 +171,127 @@ xfrog_scrub_metadata( return 0; } + +/* Decide if there have been any scrub failures up to this point. */ +static inline int +xfrog_scrubv_previous_failures( + struct xfs_scrub_vec_head *vhead, + struct xfs_scrub_vec *barrier_vec) +{ + struct xfs_scrub_vec *v; + __u32 failmask; + + failmask = barrier_vec->sv_flags & XFS_SCRUB_FLAGS_OUT; + for (v = vhead->svh_vecs; v < barrier_vec; v++) { + if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) + continue; + + /* + * Runtime errors count as a previous failure, except the ones + * used to ask userspace to retry. + */ + if (v->sv_ret && v->sv_ret != -EBUSY && v->sv_ret != -ENOENT && + v->sv_ret != -EUSERS) + return -ECANCELED; + + /* + * If any of the out-flags on the scrub vector match the mask + * that was set on the barrier vector, that's a previous fail. + */ + if (v->sv_flags & failmask) + return -ECANCELED; + } + + return 0; +} + +static int +xfrog_scrubv_fallback( + struct xfs_fd *xfd, + struct xfs_scrub_vec_head *vhead) +{ + struct xfs_scrub_vec *v; + unsigned int i; + + if (vhead->svh_flags & ~XFS_SCRUB_VEC_FLAGS_ALL) + return -EINVAL; + for (i = 0, v = vhead->svh_vecs; i < vhead->svh_nr; i++, v++) { + if (v->sv_reserved) + return -EINVAL; + if (v->sv_type == XFS_SCRUB_TYPE_BARRIER && + (v->sv_flags & ~XFS_SCRUB_FLAGS_OUT)) + return -EINVAL; + } + + /* Run all the scrubbers. */ + for (i = 0, v = vhead->svh_vecs; i < vhead->svh_nr; i++, v++) { + struct xfs_scrub_metadata sm = { + .sm_type = v->sv_type, + .sm_flags = v->sv_flags, + .sm_ino = vhead->svh_ino, + .sm_gen = vhead->svh_gen, + .sm_agno = vhead->svh_agno, + }; + struct timespec tv; + + if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) { + v->sv_ret = xfrog_scrubv_previous_failures(vhead, v); + if (v->sv_ret) + break; + continue; + } + + v->sv_ret = xfrog_scrub_metadata(xfd, &sm); + v->sv_flags = sm.sm_flags; + + if (vhead->svh_rest_us) { + tv.tv_sec = 0; + tv.tv_nsec = vhead->svh_rest_us * 1000; + nanosleep(&tv, NULL); + } + } + + return 0; +} + +/* Invoke the vectored scrub ioctl. */ +static int +xfrog_scrubv_call( + struct xfs_fd *xfd, + struct xfs_scrub_vec_head *vhead) +{ + int ret; + + ret = ioctl(xfd->fd, XFS_IOC_SCRUBV_METADATA, vhead); + if (ret) + return -errno; + + return 0; +} + +/* Invoke the vectored scrub ioctl. Returns zero or negative error code. */ +int +xfrog_scrubv_metadata( + struct xfs_fd *xfd, + struct xfs_scrub_vec_head *vhead) +{ + int error = 0; + + if (xfd->flags & XFROG_FLAG_SCRUB_FORCE_SINGLE) + goto try_single; + + error = xfrog_scrubv_call(xfd, vhead); + if (error == 0 || (xfd->flags & XFROG_FLAG_SCRUB_FORCE_VECTOR)) + return error; + + /* If the vectored scrub ioctl wasn't found, force single mode. */ + switch (error) { + case -EOPNOTSUPP: + case -ENOTTY: + xfd->flags |= XFROG_FLAG_SCRUB_FORCE_SINGLE; + break; + } + +try_single: + return xfrog_scrubv_fallback(xfd, vhead); +} diff --git a/libfrog/scrub.h b/libfrog/scrub.h index 27230c62f71..43456230479 100644 --- a/libfrog/scrub.h +++ b/libfrog/scrub.h @@ -28,4 +28,8 @@ extern const struct xfrog_scrub_descr xfrog_scrubbers[XFS_SCRUB_TYPE_NR]; int xfrog_scrub_metadata(struct xfs_fd *xfd, struct xfs_scrub_metadata *meta); +struct xfs_scrub_vec_head; + +int xfrog_scrubv_metadata(struct xfs_fd *xfd, struct xfs_scrub_vec_head *vhead); + #endif /* __LIBFROG_SCRUB_H__ */ From patchwork Sun Dec 31 23:20:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508082 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C36F6C2CC for ; Sun, 31 Dec 2023 23:20:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NT/gRM0u" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3086CC433C8; Sun, 31 Dec 2023 23:20:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064829; bh=dFAf69BYLlSKvwbZhjdef3qdavr/BewIoT0M5gM0bVQ=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=NT/gRM0uWpbo0GTy0mwL+DHrj0b/NTuB+ZKtFpnFDJdFvI6eo+oVf2LuFhHMiWjzw oYmTXmSugZEeTKfhLmWxeOaWKaiNhtrkzrrWPg42JbWcpjRtwzcM3tEFGHKzsDAhQn EQhawevnvc3GNZvjoUgffOi6GaMBmh64FBUFZwyMMldKf/9CNhnOAUNWHRN1s1GOJr Q09r/vdxWdJID4TIAgma8NVHSn29pf2mAS5uzN7xX2toBVsM/Kv4Luq8G57fnz809W u3V/Y5LnoyePCEbMYHK9GkCo6OsuH15QckNh1C2/1FpsUoUEftE8y6ljyuVpNF2mJY gOA8/tDFw2phw== Date: Sun, 31 Dec 2023 15:20:28 -0800 Subject: [PATCH 03/10] xfs_io: support vectored scrub From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007883.1806194.4040047806511956664.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Create a new scrubv command to xfs_io to support the vectored scrub ioctl. Signed-off-by: Darrick J. Wong --- io/scrub.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++------ man/man8/xfs_io.8 | 51 +++++++ 2 files changed, 382 insertions(+), 40 deletions(-) diff --git a/io/scrub.c b/io/scrub.c index a77cd872fed..456d1594f22 100644 --- a/io/scrub.c +++ b/io/scrub.c @@ -12,10 +12,13 @@ #include "libfrog/paths.h" #include "libfrog/fsgeom.h" #include "libfrog/scrub.h" +#include "libfrog/logging.h" #include "io.h" +#include "list.h" static struct cmdinfo scrub_cmd; static struct cmdinfo repair_cmd; +static const struct cmdinfo scrubv_cmd; static void scrub_help(void) @@ -197,31 +200,38 @@ parse_args( return 0; } -static int -scrub_f( - int argc, - char **argv) +static void +report_scrub_outcome( + uint32_t flags) { - struct xfs_scrub_metadata meta; - int error; - - error = parse_args(argc, argv, &scrub_cmd, &meta); - if (error) - return error; - - error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta); - if (error) - perror("scrub"); - if (meta.sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + if (flags & XFS_SCRUB_OFLAG_CORRUPT) printf(_("Corruption detected.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_PREEN) + if (flags & XFS_SCRUB_OFLAG_PREEN) printf(_("Optimization possible.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_XFAIL) + if (flags & XFS_SCRUB_OFLAG_XFAIL) printf(_("Cross-referencing failed.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_XCORRUPT) + if (flags & XFS_SCRUB_OFLAG_XCORRUPT) printf(_("Corruption detected during cross-referencing.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE) + if (flags & XFS_SCRUB_OFLAG_INCOMPLETE) printf(_("Scan was not complete.\n")); +} + +static int +scrub_f( + int argc, + char **argv) +{ + struct xfs_scrub_metadata meta; + int error; + + error = parse_args(argc, argv, &scrub_cmd, &meta); + if (error) + return error; + + error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta); + if (error) + perror("scrub"); + report_scrub_outcome(meta.sm_flags); return 0; } @@ -239,6 +249,7 @@ scrub_init(void) scrub_cmd.help = scrub_help; add_command(&scrub_cmd); + add_command(&scrubv_cmd); } static void @@ -267,34 +278,41 @@ repair_help(void) printf("\n"); } -static int -repair_f( - int argc, - char **argv) +static void +report_repair_outcome( + uint32_t flags) { - struct xfs_scrub_metadata meta; - int error; - - error = parse_args(argc, argv, &repair_cmd, &meta); - if (error) - return error; - meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR; - - error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta); - if (error) - perror("repair"); - if (meta.sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + if (flags & XFS_SCRUB_OFLAG_CORRUPT) printf(_("Corruption remains.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_PREEN) + if (flags & XFS_SCRUB_OFLAG_PREEN) printf(_("Optimization possible.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_XFAIL) + if (flags & XFS_SCRUB_OFLAG_XFAIL) printf(_("Cross-referencing failed.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_XCORRUPT) + if (flags & XFS_SCRUB_OFLAG_XCORRUPT) printf(_("Corruption still detected during cross-referencing.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE) + if (flags & XFS_SCRUB_OFLAG_INCOMPLETE) printf(_("Repair was not complete.\n")); - if (meta.sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) + if (flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) printf(_("Metadata did not need repair or optimization.\n")); +} + +static int +repair_f( + int argc, + char **argv) +{ + struct xfs_scrub_metadata meta; + int error; + + error = parse_args(argc, argv, &repair_cmd, &meta); + if (error) + return error; + meta.sm_flags |= XFS_SCRUB_IFLAG_REPAIR; + + error = ioctl(file->fd, XFS_IOC_SCRUB_METADATA, &meta); + if (error) + perror("repair"); + report_repair_outcome(meta.sm_flags); return 0; } @@ -315,3 +333,276 @@ repair_init(void) add_command(&repair_cmd); } + +static void +scrubv_help(void) +{ + printf(_( +"\n" +" Scrubs pieces of XFS filesystem metadata. The first argument is the group\n" +" of metadata to examine. If the group is 'ag', the second parameter should\n" +" be the AG number. If the group is 'inode', the second and third parameters\n" +" should be the inode number and generation number to act upon; if these are\n" +" omitted, the scrub is performed on the open file. If the group is 'fs',\n" +" 'summary', or 'probe', there are no other parameters.\n" +"\n" +" Flags are -d for debug, and -r to allow repairs.\n" +" -b NN will insert a scrub barrier after every NN scrubs, and -m sets the\n" +" desired corruption mask in all barriers. -w pauses for some microseconds\n" +" after each scrub call.\n" +"\n" +" Example:\n" +" 'scrubv ag 3' - scrub all metadata in AG 3.\n" +" 'scrubv ag 3 -b 2 -m 0x4' - scrub all metadata in AG 3, and use barriers\n" +" every third scrub to exit early if there are optimizations.\n" +" 'scrubv fs' - scrub all non-AG non-file metadata.\n" +" 'scrubv inode' - scrub all metadata for the open file.\n" +" 'scrubv inode 128 13525' - scrub all metadata for inode 128 gen 13525.\n" +" 'scrubv probe' - check for presence of online scrub.\n" +" 'scrubv summary' - scrub all summary metadata.\n")); +} + +/* Fill out the scrub vectors for a group of scrubber (ag, ino, fs, summary) */ +static void +scrubv_fill_group( + struct xfs_scrub_vec_head *vhead, + int barrier_interval, + __u32 barrier_mask, + enum xfrog_scrub_group group) +{ + const struct xfrog_scrub_descr *d; + unsigned int i; + + for (i = 0, d = xfrog_scrubbers; i < XFS_SCRUB_TYPE_NR; i++, d++) { + if (d->group != group) + continue; + vhead->svh_vecs[vhead->svh_nr++].sv_type = i; + + if (barrier_interval && + vhead->svh_nr % (barrier_interval + 1) == 0) { + struct xfs_scrub_vec *v; + + v = &vhead->svh_vecs[vhead->svh_nr++]; + v->sv_flags = barrier_mask; + v->sv_type = XFS_SCRUB_TYPE_BARRIER; + } + } +} + +/* Declare a structure big enough to handle all scrub types + barriers */ +struct scrubv_head { + struct xfs_scrub_vec_head head; + struct xfs_scrub_vec __vecs[XFS_SCRUB_TYPE_NR * 2]; +}; + + +static int +scrubv_f( + int argc, + char **argv) +{ + struct scrubv_head bighead = { }; + struct xfs_fd xfd = XFS_FD_INIT(file->fd); + struct xfs_scrub_vec_head *vhead = &bighead.head; + struct xfs_scrub_vec *v; + uint32_t flags = 0; + __u32 barrier_mask = XFS_SCRUB_OFLAG_CORRUPT; + enum xfrog_scrub_group group; + bool debug = false; + int version = -1; + int barrier_interval = 0; + int rest_us = 0; + int c; + int error; + + while ((c = getopt(argc, argv, "b:dm:rv:w:")) != EOF) { + switch (c) { + case 'b': + barrier_interval = atoi(optarg); + if (barrier_interval < 0) { + fprintf(stderr, + _("Negative barrier interval makes no sense.\n")); + exitcode = 1; + return command_usage(&scrubv_cmd); + } + break; + case 'd': + debug = true; + break; + case 'm': + barrier_mask = strtoul(optarg, NULL, 0); + break; + case 'r': + flags |= XFS_SCRUB_IFLAG_REPAIR; + break; + case 'v': + if (!strcmp("single", optarg)) { + version = 0; + } else if (!strcmp("vector", optarg)) { + version = 1; + } else { + fprintf(stderr, + _("API version must be 'single' or 'vector'.\n")); + exitcode = 1; + return command_usage(&scrubv_cmd); + } + break; + case 'w': + rest_us = atoi(optarg); + if (rest_us < 0) { + fprintf(stderr, + _("Rest time must be positive.\n")); + exitcode = 1; + return command_usage(&scrubv_cmd); + } + break; + default: + exitcode = 1; + return command_usage(&scrubv_cmd); + } + } + if (optind > argc - 1) { + fprintf(stderr, + _("Must have at least one positional argument.\n")); + exitcode = 1; + return command_usage(&scrubv_cmd); + } + + if ((flags & XFS_SCRUB_IFLAG_REPAIR) && !expert) { + printf(_("Repair flag requires expert mode.\n")); + return 1; + } + + vhead->svh_rest_us = rest_us; + for (c = 0, v = vhead->svh_vecs; c < vhead->svh_nr; c++, v++) + v->sv_flags = flags; + + /* Extract group and domain information from cmdline. */ + if (!strcmp(argv[optind], "probe")) + group = XFROG_SCRUB_GROUP_NONE; + else if (!strcmp(argv[optind], "agheader")) + group = XFROG_SCRUB_GROUP_AGHEADER; + else if (!strcmp(argv[optind], "ag")) + group = XFROG_SCRUB_GROUP_PERAG; + else if (!strcmp(argv[optind], "fs")) + group = XFROG_SCRUB_GROUP_FS; + else if (!strcmp(argv[optind], "inode")) + group = XFROG_SCRUB_GROUP_INODE; + else if (!strcmp(argv[optind], "iscan")) + group = XFROG_SCRUB_GROUP_ISCAN; + else if (!strcmp(argv[optind], "summary")) + group = XFROG_SCRUB_GROUP_SUMMARY; + else { + printf(_("Unknown group '%s'.\n"), argv[optind]); + exitcode = 1; + return command_usage(&scrubv_cmd); + } + optind++; + + switch (group) { + case XFROG_SCRUB_GROUP_INODE: + if (!parse_inode(argc, argv, optind, &vhead->svh_ino, + &vhead->svh_gen)) { + exitcode = 1; + return command_usage(&scrubv_cmd); + } + break; + case XFROG_SCRUB_GROUP_AGHEADER: + case XFROG_SCRUB_GROUP_PERAG: + if (!parse_agno(argc, argv, optind, &vhead->svh_agno)) { + exitcode = 1; + return command_usage(&scrubv_cmd); + } + break; + case XFROG_SCRUB_GROUP_FS: + case XFROG_SCRUB_GROUP_SUMMARY: + case XFROG_SCRUB_GROUP_ISCAN: + case XFROG_SCRUB_GROUP_NONE: + if (!parse_none(argc, optind)) { + exitcode = 1; + return command_usage(&scrubv_cmd); + } + break; + default: + ASSERT(0); + break; + } + scrubv_fill_group(vhead, barrier_interval, barrier_mask, group); + assert(vhead->svh_nr < ARRAY_SIZE(bighead.__vecs)); + + error = -xfd_prepare_geometry(&xfd); + if (error) { + xfrog_perror(error, "xfd_prepare_geometry"); + exitcode = 1; + return 0; + } + + switch (version) { + case 0: + xfd.flags |= XFROG_FLAG_SCRUB_FORCE_SINGLE; + break; + case 1: + xfd.flags |= XFROG_FLAG_SCRUB_FORCE_VECTOR; + break; + default: + break; + } + + error = -xfrog_scrubv_metadata(&xfd, vhead); + if (error) { + xfrog_perror(error, "xfrog_scrub_many"); + exitcode = 1; + return 0; + } + + /* Figure out what happened. */ + for (c = 0, v = vhead->svh_vecs; debug && c < vhead->svh_nr; c++, v++) { + const char *type; + + if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) + type = _("barrier"); + else + type = _(xfrog_scrubbers[v->sv_type].descr); + printf(_("[%02u] %-25s: flags 0x%x ret %d\n"), c, type, + v->sv_flags, v->sv_ret); + } + + /* Figure out what happened. */ + for (c = 0, v = vhead->svh_vecs; c < vhead->svh_nr; c++, v++) { + /* Report barrier failures. */ + if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) { + if (v->sv_ret) { + printf(_("barrier: FAILED\n")); + break; + } + continue; + } + + printf("%s: ", _(xfrog_scrubbers[v->sv_type].descr)); + switch (v->sv_ret) { + case 0: + break; + default: + printf("%s\n", strerror(-v->sv_ret)); + continue; + } + if (!(v->sv_flags & XFS_SCRUB_FLAGS_OUT)) + printf(_("OK.\n")); + else if (v->sv_flags & XFS_SCRUB_IFLAG_REPAIR) + report_repair_outcome(v->sv_flags); + else + report_scrub_outcome(v->sv_flags); + } + + return 0; +} + +static const struct cmdinfo scrubv_cmd = { + .name = "scrubv", + .cfunc = scrubv_f, + .argmin = 1, + .argmax = -1, + .flags = CMD_NOMAP_OK, + .oneline = N_("vectored metadata scrub"), + .help = scrubv_help, +}; diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8 index aa9907c1e5e..5a6b2724504 100644 --- a/man/man8/xfs_io.8 +++ b/man/man8/xfs_io.8 @@ -1428,6 +1428,57 @@ inode number and generation number are specified. .RE .PD .TP +.BI "scrubv [ \-b NN ] [ \-d ] [ \-f ] [ \-r ] [ \-v NN ] [ \-w ms ] " group " [ " agnumber " | " "ino" " " "gen" " ]" +Scrub a bunch of internal XFS filesystem metadata. +The +.BI group +parameter specifies which group of metadata to scrub. +Valid groups are +.IR ag ", " agheader ", " inode ", " iscan ", " fs ", " probe ", " rtgroup ", or " summary . + +For +.BR ag " and " agheader +metadata, one AG number must be specified. +For +.B inode +metadata, the scrub is applied to the open file unless the +inode number and generation number are specified. +For +.B rtgroup +metadata, one rt group number must be specified. + +.RS 1.0i +.PD 0 +.TP +.BI "\-b " NN +Inject scrub barriers into the vector stream at the given interval. +Barriers abort vector processing if any previous scrub function found +corruption. +.TP +.BI \-d +Enables debug mode. +.TP +.BI \-f +Permit the kernel to freeze the filesystem in order to scrub or repair. +.TP +.BI \-r +Repair metadata if corruptions are found. +This option requires expert mode. +.TP +.BI "\-v " NN +Force a particular API version. +.B single +selects XFS_SCRUB_METADATA (one-by-one). +.B vector +selects XFS_SCRUBV_METADATA (vectored). +If no option is specified, vector mode will be used, with a fallback to single +mode if the kernel doesn't recognize the vector mode ioctl. +.TP +.BI "\-w " us +Wait the given number of microseconds between each scrub function. +.RE +.PD +.TP .BI "repair " type " [ " agnumber " | " "ino" " " "gen" " ]" Repair internal XFS filesystem metadata. The .BI type From patchwork Sun Dec 31 23:20:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508083 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0F3F6C2D4 for ; Sun, 31 Dec 2023 23:20:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UJKhHUYi" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D06CEC433C7; Sun, 31 Dec 2023 23:20:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064844; bh=iFg5y5bJBv2Zu0qkjo8DB3chWZdmc9qW+a0QrfO/EpA=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=UJKhHUYi9JAD50zHHkeMgp9IhoHWIGGIIat5fw5jBuMB/YFHyfIEKEDL6hNsZZ/De nUAoWMJNpCqNPsn3uJBNMlPsk4srQ6+o2jPGzkZbIt96uIfggibHU4ASGbiCGurNrH Kbqmh97/MCJqog/GqK6/oZlHaSrAyYgc2TAJP7mVOxz5cnVpNRjXJqFnNYoUH3oB2f mg1vmlKo336bx8cB8ouPE9iLgKCJWNC5ubkJlpoE6qdIXci6pdFBXk4ScrzOdlty6O GrmWNmbhXNcE/nfJZTB5ZGQBuVG2+dpuCtSUdLSnFp8bdyHnRDn2Z0njTKTPXfWXS4 AYM16QcI1l/lg== Date: Sun, 31 Dec 2023 15:20:44 -0800 Subject: [PATCH 04/10] xfs_scrub: split the scrub epilogue code into a separate function From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007896.1806194.3798646198924879346.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Move all the code that updates the internal state in response to a scrub ioctl() call completion into a separate function. This will help with vectorizing scrub calls later on. Signed-off-by: Darrick J. Wong --- scrub/scrub.c | 52 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/scrub/scrub.c b/scrub/scrub.c index 1b0609e7418..c4b4367e458 100644 --- a/scrub/scrub.c +++ b/scrub/scrub.c @@ -22,6 +22,10 @@ #include "descr.h" #include "scrub_private.h" +static int scrub_epilogue(struct scrub_ctx *ctx, struct descr *dsc, + struct scrub_item *sri, struct xfs_scrub_metadata *meta, + int error); + /* Online scrub and repair wrappers. */ /* Format a scrub description. */ @@ -117,12 +121,32 @@ xfs_check_metadata( dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags); error = -xfrog_scrub_metadata(xfdp, &meta); + return scrub_epilogue(ctx, &dsc, sri, &meta, error); +} + +/* + * Update all internal state after a scrub ioctl call. + * Returns 0 for success, or ECANCELED to abort the program. + */ +static int +scrub_epilogue( + struct scrub_ctx *ctx, + struct descr *dsc, + struct scrub_item *sri, + struct xfs_scrub_metadata *meta, + int error) +{ + unsigned int scrub_type = meta->sm_type; + enum xfrog_scrub_group group; + + group = xfrog_scrubbers[scrub_type].group; + switch (error) { case 0: /* No operational errors encountered. */ if (!sri->sri_revalidate && debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) - meta.sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; + meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; break; case ENOENT: /* Metadata not present, just skip it. */ @@ -130,13 +154,13 @@ xfs_check_metadata( return 0; case ESHUTDOWN: /* FS already crashed, give up. */ - str_error(ctx, descr_render(&dsc), + str_error(ctx, descr_render(dsc), _("Filesystem is shut down, aborting.")); return ECANCELED; case EIO: case ENOMEM: /* Abort on I/O errors or insufficient memory. */ - str_liberror(ctx, error, descr_render(&dsc)); + str_liberror(ctx, error, descr_render(dsc)); return ECANCELED; case EDEADLOCK: case EBUSY: @@ -152,7 +176,7 @@ _("Filesystem is shut down, aborting.")); return 0; default: /* Operational error. Log it and move on. */ - str_liberror(ctx, error, descr_render(&dsc)); + str_liberror(ctx, error, descr_render(dsc)); scrub_item_clean_state(sri, scrub_type); return 0; } @@ -163,27 +187,27 @@ _("Filesystem is shut down, aborting.")); * we'll try the scan again, just in case the fs was busy. * Only retry so many times. */ - if (want_retry(&meta) && scrub_item_schedule_retry(sri, scrub_type)) + if (want_retry(meta) && scrub_item_schedule_retry(sri, scrub_type)) return 0; /* Complain about incomplete or suspicious metadata. */ - scrub_warn_incomplete_scrub(ctx, &dsc, &meta); + scrub_warn_incomplete_scrub(ctx, dsc, meta); /* * If we need repairs or there were discrepancies, schedule a * repair if desired, otherwise complain. */ - if (is_corrupt(&meta) || xref_disagrees(&meta)) { + if (is_corrupt(meta) || xref_disagrees(meta)) { if (ctx->mode != SCRUB_MODE_REPAIR) { /* Dry-run mode, so log an error and forget it. */ - str_corrupt(ctx, descr_render(&dsc), + str_corrupt(ctx, descr_render(dsc), _("Repairs are required.")); scrub_item_clean_state(sri, scrub_type); return 0; } /* Schedule repairs. */ - scrub_item_save_state(sri, scrub_type, meta.sm_flags); + scrub_item_save_state(sri, scrub_type, meta->sm_flags); return 0; } @@ -191,12 +215,12 @@ _("Repairs are required.")); * If we could optimize, schedule a repair if desired, * otherwise complain. */ - if (is_unoptimized(&meta)) { + if (is_unoptimized(meta)) { if (ctx->mode == SCRUB_MODE_DRY_RUN) { /* Dry-run mode, so log an error and forget it. */ if (group != XFROG_SCRUB_GROUP_INODE) { /* AG or FS metadata, always warn. */ - str_info(ctx, descr_render(&dsc), + str_info(ctx, descr_render(dsc), _("Optimization is possible.")); } else if (!ctx->preen_triggers[scrub_type]) { /* File metadata, only warn once per type. */ @@ -210,7 +234,7 @@ _("Optimization is possible.")); } /* Schedule optimizations. */ - scrub_item_save_state(sri, scrub_type, meta.sm_flags); + scrub_item_save_state(sri, scrub_type, meta->sm_flags); return 0; } @@ -221,8 +245,8 @@ _("Optimization is possible.")); * re-examine the object as repairs progress to see if the kernel will * deem it completely consistent at some point. */ - if (xref_failed(&meta) && ctx->mode == SCRUB_MODE_REPAIR) { - scrub_item_save_state(sri, scrub_type, meta.sm_flags); + if (xref_failed(meta) && ctx->mode == SCRUB_MODE_REPAIR) { + scrub_item_save_state(sri, scrub_type, meta->sm_flags); return 0; } From patchwork Sun Dec 31 23:21:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508084 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A9D61C2D4 for ; Sun, 31 Dec 2023 23:21:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="frquTU1w" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 79134C433C8; Sun, 31 Dec 2023 23:21:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064860; bh=eReDNuA5zbF7MCYE4cLHGU2ufzck1Ahu/pvpbJFQVDM=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=frquTU1wu1rFcJheO/qjYZjgziI+YwqrJDt5/6g/kBDmq2Y7+HyT0D43FA0OTn13n R44m4nRZd10Cg8vwnQfA/o1gwO6iTX97bWqY9TsF0e5mPpKlD2XwvXxgjq/mP0eVj6 GYbZ6XcumWjcQK5BfPpWTs6dB2o2XT8g85BqhvTo42DJP9yiIu7TjKW3n498YzpHTs 0XX/q/hK2x9zE43Np3QZTnoi+pZ6Y4+bS0iDN1i04PfpomsU9gwJ6y5nRfZDY1zyB4 D5WEQup1uXsWSZnClc6wYPmtt3yRGmF/uo24Pd3buioz9042+JeMCk2iaaGCkDOMuo rYeBqeLVDFUqw== Date: Sun, 31 Dec 2023 15:21:00 -0800 Subject: [PATCH 05/10] xfs_scrub: split the repair epilogue code into a separate function From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007909.1806194.6961016643522301219.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Move all the code that updates the internal state in response to a repair ioctl() call completion into a separate function. This will help with vectorizing repair calls later on. Signed-off-by: Darrick J. Wong --- scrub/repair.c | 69 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/scrub/repair.c b/scrub/repair.c index 4fed86134ed..0b99e335191 100644 --- a/scrub/repair.c +++ b/scrub/repair.c @@ -20,6 +20,11 @@ #include "descr.h" #include "scrub_private.h" +static int repair_epilogue(struct scrub_ctx *ctx, struct descr *dsc, + struct scrub_item *sri, unsigned int repair_flags, + struct xfs_scrub_metadata *oldm, + struct xfs_scrub_metadata *meta, int error); + /* General repair routines. */ /* @@ -133,6 +138,22 @@ xfs_repair_metadata( _("Attempting optimization.")); error = -xfrog_scrub_metadata(xfdp, &meta); + return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &meta, + error); +} + +static int +repair_epilogue( + struct scrub_ctx *ctx, + struct descr *dsc, + struct scrub_item *sri, + unsigned int repair_flags, + struct xfs_scrub_metadata *oldm, + struct xfs_scrub_metadata *meta, + int error) +{ + unsigned int scrub_type = meta->sm_type; + switch (error) { case 0: /* No operational errors encountered. */ @@ -141,12 +162,12 @@ xfs_repair_metadata( case EBUSY: /* Filesystem is busy, try again later. */ if (debug || verbose) - str_info(ctx, descr_render(&dsc), + str_info(ctx, descr_render(dsc), _("Filesystem is busy, deferring repair.")); return 0; case ESHUTDOWN: /* Filesystem is already shut down, abort. */ - str_error(ctx, descr_render(&dsc), + str_error(ctx, descr_render(dsc), _("Filesystem is shut down, aborting.")); return ECANCELED; case ENOTTY: @@ -157,7 +178,7 @@ _("Filesystem is shut down, aborting.")); * how to perform the repair, don't requeue the request. Mark * it done and move on. */ - if (is_unoptimized(&oldm) || + if (is_unoptimized(oldm) || debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) { scrub_item_clean_state(sri, scrub_type); return 0; @@ -175,14 +196,14 @@ _("Filesystem is shut down, aborting.")); fallthrough; case EINVAL: /* Kernel doesn't know how to repair this? */ - str_corrupt(ctx, descr_render(&dsc), + str_corrupt(ctx, descr_render(dsc), _("Don't know how to fix; offline repair required.")); scrub_item_clean_state(sri, scrub_type); return 0; case EROFS: /* Read-only filesystem, can't fix. */ - if (verbose || debug || needs_repair(&oldm)) - str_error(ctx, descr_render(&dsc), + if (verbose || debug || needs_repair(oldm)) + str_error(ctx, descr_render(dsc), _("Read-only filesystem; cannot make changes.")); return ECANCELED; case ENOENT: @@ -192,7 +213,7 @@ _("Read-only filesystem; cannot make changes.")); case ENOMEM: case ENOSPC: /* Don't care if preen fails due to low resources. */ - if (is_unoptimized(&oldm) && !needs_repair(&oldm)) { + if (is_unoptimized(oldm) && !needs_repair(oldm)) { scrub_item_clean_state(sri, scrub_type); return 0; } @@ -207,7 +228,7 @@ _("Read-only filesystem; cannot make changes.")); */ if (!(repair_flags & XRM_FINAL_WARNING)) return 0; - str_liberror(ctx, error, descr_render(&dsc)); + str_liberror(ctx, error, descr_render(dsc)); scrub_item_clean_state(sri, scrub_type); return 0; } @@ -218,12 +239,12 @@ _("Read-only filesystem; cannot make changes.")); * the repair again, just in case the fs was busy. Only retry so many * times. */ - if (want_retry(&meta) && scrub_item_schedule_retry(sri, scrub_type)) + if (want_retry(meta) && scrub_item_schedule_retry(sri, scrub_type)) return 0; if (repair_flags & XRM_FINAL_WARNING) - scrub_warn_incomplete_scrub(ctx, &dsc, &meta); - if (needs_repair(&meta) || is_incomplete(&meta)) { + scrub_warn_incomplete_scrub(ctx, dsc, meta); + if (needs_repair(meta) || is_incomplete(meta)) { /* * Still broken; if we've been told not to complain then we * just requeue this and try again later. Otherwise we @@ -231,9 +252,9 @@ _("Read-only filesystem; cannot make changes.")); */ if (!(repair_flags & XRM_FINAL_WARNING)) return 0; - str_corrupt(ctx, descr_render(&dsc), + str_corrupt(ctx, descr_render(dsc), _("Repair unsuccessful; offline repair required.")); - } else if (xref_failed(&meta)) { + } else if (xref_failed(meta)) { /* * This metadata object itself looks ok, but we still noticed * inconsistencies when comparing it with the other filesystem @@ -242,31 +263,31 @@ _("Repair unsuccessful; offline repair required.")); * reverify the cross-referencing as repairs progress. */ if (repair_flags & XRM_FINAL_WARNING) { - str_info(ctx, descr_render(&dsc), + str_info(ctx, descr_render(dsc), _("Seems correct but cross-referencing failed; offline repair recommended.")); } else { if (verbose) - str_info(ctx, descr_render(&dsc), + str_info(ctx, descr_render(dsc), _("Seems correct but cross-referencing failed; will keep checking.")); return 0; } - } else if (meta.sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) { + } else if (meta->sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) { if (verbose) - str_info(ctx, descr_render(&dsc), + str_info(ctx, descr_render(dsc), _("No modification needed.")); } else { /* Clean operation, no corruption detected. */ - if (is_corrupt(&oldm)) - record_repair(ctx, descr_render(&dsc), + if (is_corrupt(oldm)) + record_repair(ctx, descr_render(dsc), _("Repairs successful.")); - else if (xref_disagrees(&oldm)) - record_repair(ctx, descr_render(&dsc), + else if (xref_disagrees(oldm)) + record_repair(ctx, descr_render(dsc), _("Repairs successful after discrepancy in cross-referencing.")); - else if (xref_failed(&oldm)) - record_repair(ctx, descr_render(&dsc), + else if (xref_failed(oldm)) + record_repair(ctx, descr_render(dsc), _("Repairs successful after cross-referencing failure.")); else - record_preen(ctx, descr_render(&dsc), + record_preen(ctx, descr_render(dsc), _("Optimization successful.")); } From patchwork Sun Dec 31 23:21:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508085 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 87B5BC2C0 for ; Sun, 31 Dec 2023 23:21:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YAPHtO7L" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 153C1C433C7; Sun, 31 Dec 2023 23:21:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064876; bh=sd58QA+xdwFy8ktarkAyrpavhOWVfkA/oM/SVGY+IKE=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=YAPHtO7LpnX/tYFOlUCsbZsCO3ey+eKhPENEtTYSoSw71vUZxO35d5HlrFiYGBTTx I1vyTEtPbsfBbSWfl9P5H16ToS5qhrGAMyh31/d0/MUawBcXh5Z5juDE2FgUzYzbN6 dSHoIYIYWT4njhSkZ7StJ4XBDdgdO3WYMqCt5fLDH2gRr0cy9ddzpA68riv4qnfK8f X7vkh2vXojQdEtKyErqBWLGs+hq89+HJF0tdlLuIBS6SDI3UcO8kvLz8SsU4LwlUDv /KV3WCA/di5mrZT4HEHRfjiPVuSYZ+MKKcJRyyvFiDqhIxfC7aMl39g29xL2yPIlpf T/a046q/BtSlA== Date: Sun, 31 Dec 2023 15:21:15 -0800 Subject: [PATCH 06/10] xfs_scrub: convert scrub and repair epilogues to use xfs_scrub_vec From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007922.1806194.16626028776825292187.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Convert the scrub and repair epilogue code to pass around xfs_scrub_vecs as we prepare for vectorized operation. Signed-off-by: Darrick J. Wong --- scrub/repair.c | 35 ++++++++++++++++++----------------- scrub/scrub.c | 27 ++++++++++++++------------- scrub/scrub_private.h | 34 +++++++++++++++++----------------- 3 files changed, 49 insertions(+), 47 deletions(-) diff --git a/scrub/repair.c b/scrub/repair.c index 0b99e335191..7a710a159e6 100644 --- a/scrub/repair.c +++ b/scrub/repair.c @@ -22,8 +22,8 @@ static int repair_epilogue(struct scrub_ctx *ctx, struct descr *dsc, struct scrub_item *sri, unsigned int repair_flags, - struct xfs_scrub_metadata *oldm, - struct xfs_scrub_metadata *meta, int error); + const struct xfs_scrub_vec *oldm, + const struct xfs_scrub_vec *meta); /* General repair routines. */ @@ -93,10 +93,9 @@ xfs_repair_metadata( unsigned int repair_flags) { struct xfs_scrub_metadata meta = { 0 }; - struct xfs_scrub_metadata oldm; + struct xfs_scrub_vec oldm, vec; DEFINE_DESCR(dsc, ctx, format_scrub_descr); bool repair_only; - int error; /* * If the caller boosted the priority of this scrub type on behalf of a @@ -124,22 +123,24 @@ xfs_repair_metadata( break; } - if (!is_corrupt(&meta) && repair_only) + vec.sv_type = scrub_type; + vec.sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY; + memcpy(&oldm, &vec, sizeof(struct xfs_scrub_vec)); + if (!is_corrupt(&vec) && repair_only) return 0; - memcpy(&oldm, &meta, sizeof(oldm)); - oldm.sm_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY; - descr_set(&dsc, &oldm); + descr_set(&dsc, &meta); - if (needs_repair(&oldm)) + if (needs_repair(&vec)) str_info(ctx, descr_render(&dsc), _("Attempting repair.")); else if (debug || verbose) str_info(ctx, descr_render(&dsc), _("Attempting optimization.")); - error = -xfrog_scrub_metadata(xfdp, &meta); - return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &meta, - error); + vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta); + vec.sv_flags = meta.sm_flags; + + return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &vec); } static int @@ -148,11 +149,11 @@ repair_epilogue( struct descr *dsc, struct scrub_item *sri, unsigned int repair_flags, - struct xfs_scrub_metadata *oldm, - struct xfs_scrub_metadata *meta, - int error) + const struct xfs_scrub_vec *oldm, + const struct xfs_scrub_vec *meta) { - unsigned int scrub_type = meta->sm_type; + unsigned int scrub_type = meta->sv_type; + int error = -meta->sv_ret; switch (error) { case 0: @@ -271,7 +272,7 @@ _("Repair unsuccessful; offline repair required.")); _("Seems correct but cross-referencing failed; will keep checking.")); return 0; } - } else if (meta->sm_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) { + } else if (meta->sv_flags & XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED) { if (verbose) str_info(ctx, descr_render(dsc), _("No modification needed.")); diff --git a/scrub/scrub.c b/scrub/scrub.c index c4b4367e458..2fb2293558e 100644 --- a/scrub/scrub.c +++ b/scrub/scrub.c @@ -23,8 +23,7 @@ #include "scrub_private.h" static int scrub_epilogue(struct scrub_ctx *ctx, struct descr *dsc, - struct scrub_item *sri, struct xfs_scrub_metadata *meta, - int error); + struct scrub_item *sri, struct xfs_scrub_vec *vec); /* Online scrub and repair wrappers. */ @@ -62,7 +61,7 @@ void scrub_warn_incomplete_scrub( struct scrub_ctx *ctx, struct descr *dsc, - struct xfs_scrub_metadata *meta) + const struct xfs_scrub_vec *meta) { if (is_incomplete(meta)) str_info(ctx, descr_render(dsc), _("Check incomplete.")); @@ -91,8 +90,8 @@ xfs_check_metadata( { DEFINE_DESCR(dsc, ctx, format_scrub_descr); struct xfs_scrub_metadata meta = { }; + struct xfs_scrub_vec vec; enum xfrog_scrub_group group; - int error; background_sleep(); @@ -120,8 +119,10 @@ xfs_check_metadata( dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags); - error = -xfrog_scrub_metadata(xfdp, &meta); - return scrub_epilogue(ctx, &dsc, sri, &meta, error); + vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta); + vec.sv_type = scrub_type; + vec.sv_flags = meta.sm_flags; + return scrub_epilogue(ctx, &dsc, sri, &vec); } /* @@ -133,11 +134,11 @@ scrub_epilogue( struct scrub_ctx *ctx, struct descr *dsc, struct scrub_item *sri, - struct xfs_scrub_metadata *meta, - int error) + struct xfs_scrub_vec *meta) { - unsigned int scrub_type = meta->sm_type; + unsigned int scrub_type = meta->sv_type; enum xfrog_scrub_group group; + int error = -meta->sv_ret; group = xfrog_scrubbers[scrub_type].group; @@ -146,7 +147,7 @@ scrub_epilogue( /* No operational errors encountered. */ if (!sri->sri_revalidate && debug_tweak_on("XFS_SCRUB_FORCE_REPAIR")) - meta->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; + meta->sv_flags |= XFS_SCRUB_OFLAG_CORRUPT; break; case ENOENT: /* Metadata not present, just skip it. */ @@ -207,7 +208,7 @@ _("Repairs are required.")); } /* Schedule repairs. */ - scrub_item_save_state(sri, scrub_type, meta->sm_flags); + scrub_item_save_state(sri, scrub_type, meta->sv_flags); return 0; } @@ -234,7 +235,7 @@ _("Optimization is possible.")); } /* Schedule optimizations. */ - scrub_item_save_state(sri, scrub_type, meta->sm_flags); + scrub_item_save_state(sri, scrub_type, meta->sv_flags); return 0; } @@ -246,7 +247,7 @@ _("Optimization is possible.")); * deem it completely consistent at some point. */ if (xref_failed(meta) && ctx->mode == SCRUB_MODE_REPAIR) { - scrub_item_save_state(sri, scrub_type, meta->sm_flags); + scrub_item_save_state(sri, scrub_type, meta->sv_flags); return 0; } diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h index bcfabda16be..98a9238f2aa 100644 --- a/scrub/scrub_private.h +++ b/scrub/scrub_private.h @@ -13,40 +13,40 @@ int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen, /* Predicates for scrub flag state. */ -static inline bool is_corrupt(struct xfs_scrub_metadata *sm) +static inline bool is_corrupt(const struct xfs_scrub_vec *sv) { - return sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT; + return sv->sv_flags & XFS_SCRUB_OFLAG_CORRUPT; } -static inline bool is_unoptimized(struct xfs_scrub_metadata *sm) +static inline bool is_unoptimized(const struct xfs_scrub_vec *sv) { - return sm->sm_flags & XFS_SCRUB_OFLAG_PREEN; + return sv->sv_flags & XFS_SCRUB_OFLAG_PREEN; } -static inline bool xref_failed(struct xfs_scrub_metadata *sm) +static inline bool xref_failed(const struct xfs_scrub_vec *sv) { - return sm->sm_flags & XFS_SCRUB_OFLAG_XFAIL; + return sv->sv_flags & XFS_SCRUB_OFLAG_XFAIL; } -static inline bool xref_disagrees(struct xfs_scrub_metadata *sm) +static inline bool xref_disagrees(const struct xfs_scrub_vec *sv) { - return sm->sm_flags & XFS_SCRUB_OFLAG_XCORRUPT; + return sv->sv_flags & XFS_SCRUB_OFLAG_XCORRUPT; } -static inline bool is_incomplete(struct xfs_scrub_metadata *sm) +static inline bool is_incomplete(const struct xfs_scrub_vec *sv) { - return sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE; + return sv->sv_flags & XFS_SCRUB_OFLAG_INCOMPLETE; } -static inline bool is_suspicious(struct xfs_scrub_metadata *sm) +static inline bool is_suspicious(const struct xfs_scrub_vec *sv) { - return sm->sm_flags & XFS_SCRUB_OFLAG_WARNING; + return sv->sv_flags & XFS_SCRUB_OFLAG_WARNING; } /* Should we fix it? */ -static inline bool needs_repair(struct xfs_scrub_metadata *sm) +static inline bool needs_repair(const struct xfs_scrub_vec *sv) { - return is_corrupt(sm) || xref_disagrees(sm); + return is_corrupt(sv) || xref_disagrees(sv); } /* @@ -54,13 +54,13 @@ static inline bool needs_repair(struct xfs_scrub_metadata *sm) * scan/repair; or if there were cross-referencing problems but the object was * not obviously corrupt. */ -static inline bool want_retry(struct xfs_scrub_metadata *sm) +static inline bool want_retry(const struct xfs_scrub_vec *sv) { - return is_incomplete(sm) || (xref_disagrees(sm) && !is_corrupt(sm)); + return is_incomplete(sv) || (xref_disagrees(sv) && !is_corrupt(sv)); } void scrub_warn_incomplete_scrub(struct scrub_ctx *ctx, struct descr *dsc, - struct xfs_scrub_metadata *meta); + const struct xfs_scrub_vec *meta); /* Scrub item functions */ From patchwork Sun Dec 31 23:21:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508086 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4D0AEC2C5 for ; Sun, 31 Dec 2023 23:21:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="IS8Hiphu" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AF92FC433C8; Sun, 31 Dec 2023 23:21:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064891; bh=NYKMGv6tmddyA3UelwTfD5eeBxbgTg9YcinI/7gBwoY=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=IS8HiphutYgThhcFHZK0/ZIpnzLx+y/u4A25YGYDeJl6+pZVCL/iuUmA0I5KMMrf3 Sud3oSG+wyJXWBMKka9BEloMK2q7cC2PC1PkJn2PM16pK+kcSLr5l/Nb0QMdadYj5p VgrZ+P8MYdVz7Kp/3Gs23JjukPJwFnqp9sZjgls5dOBW8G+bqRDBEE1HG4wqFRv8iC 2tnyc+vXPNq1+Z+kedSWHjCYqqT5QNT3oIr9y4CJyoD/UZyjz1BweBYVq8By7Z4VSg 3S1pmYGQkvm1xgHAx+pGAQRh6mZhoxTFDd3HK/43lyfN7/b/WjqiNU1jICWB7OPhnO 9MrZH2ewcuADQ== Date: Sun, 31 Dec 2023 15:21:31 -0800 Subject: [PATCH 07/10] xfs_scrub: vectorize scrub calls From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007936.1806194.7369999728267504344.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Use the new vectorized kernel scrub calls to reduce the overhead of checking metadata. Signed-off-by: Darrick J. Wong --- scrub/phase1.c | 2 scrub/scrub.c | 261 +++++++++++++++++++++++++++++++++++-------------- scrub/scrub.h | 2 scrub/scrub_private.h | 21 ++++ 4 files changed, 213 insertions(+), 73 deletions(-) diff --git a/scrub/phase1.c b/scrub/phase1.c index 095c045915a..091b59e57e7 100644 --- a/scrub/phase1.c +++ b/scrub/phase1.c @@ -216,6 +216,8 @@ _("Kernel metadata scrubbing facility is not available.")); return ECANCELED; } + check_scrubv(ctx); + /* * Normally, callers are required to pass -n if the provided path is a * readonly filesystem or the kernel wasn't built with online repair diff --git a/scrub/scrub.c b/scrub/scrub.c index 2fb2293558e..ec8d5e92cea 100644 --- a/scrub/scrub.c +++ b/scrub/scrub.c @@ -22,11 +22,39 @@ #include "descr.h" #include "scrub_private.h" -static int scrub_epilogue(struct scrub_ctx *ctx, struct descr *dsc, - struct scrub_item *sri, struct xfs_scrub_vec *vec); - /* Online scrub and repair wrappers. */ +/* Describe the current state of a vectored scrub. */ +static int +format_scrubv_descr( + struct scrub_ctx *ctx, + char *buf, + size_t buflen, + void *where) +{ + struct scrubv_head *bh = where; + struct xfs_scrub_vec_head *vhead = &bh->head; + struct xfs_scrub_vec *v = bh->head.svh_vecs + bh->i; + const struct xfrog_scrub_descr *sc = &xfrog_scrubbers[v->sv_type]; + + switch (sc->group) { + case XFROG_SCRUB_GROUP_AGHEADER: + case XFROG_SCRUB_GROUP_PERAG: + return snprintf(buf, buflen, _("AG %u %s"), vhead->svh_agno, + _(sc->descr)); + case XFROG_SCRUB_GROUP_INODE: + return scrub_render_ino_descr(ctx, buf, buflen, + vhead->svh_ino, vhead->svh_gen, "%s", + _(sc->descr)); + case XFROG_SCRUB_GROUP_FS: + case XFROG_SCRUB_GROUP_SUMMARY: + case XFROG_SCRUB_GROUP_ISCAN: + case XFROG_SCRUB_GROUP_NONE: + return snprintf(buf, buflen, _("%s"), _(sc->descr)); + } + return -1; +} + /* Format a scrub description. */ int format_scrub_descr( @@ -80,51 +108,6 @@ scrub_warn_incomplete_scrub( _("Cross-referencing failed.")); } -/* Do a read-only check of some metadata. */ -static int -xfs_check_metadata( - struct scrub_ctx *ctx, - struct xfs_fd *xfdp, - unsigned int scrub_type, - struct scrub_item *sri) -{ - DEFINE_DESCR(dsc, ctx, format_scrub_descr); - struct xfs_scrub_metadata meta = { }; - struct xfs_scrub_vec vec; - enum xfrog_scrub_group group; - - background_sleep(); - - group = xfrog_scrubbers[scrub_type].group; - meta.sm_type = scrub_type; - switch (group) { - case XFROG_SCRUB_GROUP_AGHEADER: - case XFROG_SCRUB_GROUP_PERAG: - meta.sm_agno = sri->sri_agno; - break; - case XFROG_SCRUB_GROUP_FS: - case XFROG_SCRUB_GROUP_SUMMARY: - case XFROG_SCRUB_GROUP_ISCAN: - case XFROG_SCRUB_GROUP_NONE: - break; - case XFROG_SCRUB_GROUP_INODE: - meta.sm_ino = sri->sri_ino; - meta.sm_gen = sri->sri_gen; - break; - } - - assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); - assert(scrub_type < XFS_SCRUB_TYPE_NR); - descr_set(&dsc, &meta); - - dbg_printf("check %s flags %xh\n", descr_render(&dsc), meta.sm_flags); - - vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta); - vec.sv_type = scrub_type; - vec.sv_flags = meta.sm_flags; - return scrub_epilogue(ctx, &dsc, sri, &vec); -} - /* * Update all internal state after a scrub ioctl call. * Returns 0 for success, or ECANCELED to abort the program. @@ -256,6 +239,88 @@ _("Optimization is possible.")); return 0; } +/* Fill out the scrub vector header. */ +void +scrub_item_to_vhead( + struct scrubv_head *bighead, + const struct scrub_item *sri) +{ + struct xfs_scrub_vec_head *vhead = &bighead->head; + + if (bg_mode > 1) + vhead->svh_rest_us = bg_mode - 1; + if (sri->sri_agno != -1) + vhead->svh_agno = sri->sri_agno; + if (sri->sri_ino != -1ULL) { + vhead->svh_ino = sri->sri_ino; + vhead->svh_gen = sri->sri_gen; + } +} + +/* Add a scrubber to the scrub vector. */ +void +scrub_vhead_add( + struct scrubv_head *bighead, + const struct scrub_item *sri, + unsigned int scrub_type) +{ + struct xfs_scrub_vec_head *vhead = &bighead->head; + struct xfs_scrub_vec *v; + + v = &vhead->svh_vecs[vhead->svh_nr++]; + v->sv_type = scrub_type; + bighead->i = v - vhead->svh_vecs; +} + +/* Do a read-only check of some metadata. */ +static int +scrub_call_kernel( + struct scrub_ctx *ctx, + struct xfs_fd *xfdp, + struct scrub_item *sri) +{ + DEFINE_DESCR(dsc, ctx, format_scrubv_descr); + struct scrubv_head bh = { }; + struct xfs_scrub_vec *v; + unsigned int scrub_type; + int error; + + assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); + + scrub_item_to_vhead(&bh, sri); + descr_set(&dsc, &bh); + + foreach_scrub_type(scrub_type) { + if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK)) + continue; + scrub_vhead_add(&bh, sri, scrub_type); + + dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc), + sri->sri_state[scrub_type], + sri->sri_tries[scrub_type]); + } + + error = -xfrog_scrubv_metadata(xfdp, &bh.head); + if (error) + return error; + + foreach_bighead_vec(&bh, v) { + error = scrub_epilogue(ctx, &dsc, sri, v); + if (error) + return error; + + /* + * Progress is counted by the inode for inode metadata; for + * everything else, it's counted for each scrub call. + */ + if (!(sri->sri_state[v->sv_type] & SCRUB_ITEM_NEEDSCHECK) && + sri->sri_ino == -1ULL) + progress_add(1); + } + + return 0; +} + /* Bulk-notify user about things that could be optimized. */ void scrub_report_preen_triggers( @@ -291,6 +356,37 @@ scrub_item_schedule_group( } } +/* Decide if we call the kernel again to finish scrub/repair activity. */ +static inline bool +scrub_item_call_kernel_again_future( + struct scrub_item *sri, + uint8_t work_mask, + const struct scrub_item *old) +{ + unsigned int scrub_type; + unsigned int nr = 0; + + /* If there's nothing to do, we're done. */ + foreach_scrub_type(scrub_type) { + if (sri->sri_state[scrub_type] & work_mask) + nr++; + } + if (!nr) + return false; + + foreach_scrub_type(scrub_type) { + uint8_t statex = sri->sri_state[scrub_type] ^ + old->sri_state[scrub_type]; + + if (statex & work_mask) + return true; + if (sri->sri_tries[scrub_type] != old->sri_tries[scrub_type]) + return true; + } + + return false; +} + /* Decide if we call the kernel again to finish scrub/repair activity. */ bool scrub_item_call_kernel_again( @@ -319,6 +415,29 @@ scrub_item_call_kernel_again( return false; } +/* + * For each scrub item whose state matches the state_flags, set up the item + * state for a kernel call. Returns true if any work was scheduled. + */ +bool +scrub_item_schedule_work( + struct scrub_item *sri, + uint8_t state_flags) +{ + unsigned int scrub_type; + unsigned int nr = 0; + + foreach_scrub_type(scrub_type) { + if (!(sri->sri_state[scrub_type] & state_flags)) + continue; + + sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES; + nr++; + } + + return nr > 0; +} + /* Run all the incomplete scans on this scrub principal. */ int scrub_item_check_file( @@ -329,8 +448,10 @@ scrub_item_check_file( struct xfs_fd xfd; struct scrub_item old_sri; struct xfs_fd *xfdp = &ctx->mnt; - unsigned int scrub_type; - int error; + int error = 0; + + if (!scrub_item_schedule_work(sri, SCRUB_ITEM_NEEDSCHECK)) + return 0; /* * If the caller passed us a file descriptor for a scrub, use it @@ -343,31 +464,15 @@ scrub_item_check_file( xfdp = &xfd; } - foreach_scrub_type(scrub_type) { - if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK)) - continue; - - sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES; - do { - memcpy(&old_sri, sri, sizeof(old_sri)); - error = xfs_check_metadata(ctx, xfdp, scrub_type, sri); - if (error) - return error; - } while (scrub_item_call_kernel_again(sri, scrub_type, - SCRUB_ITEM_NEEDSCHECK, &old_sri)); - - /* - * Progress is counted by the inode for inode metadata; for - * everything else, it's counted for each scrub call. - */ - if (sri->sri_ino == -1ULL) - progress_add(1); - + do { + memcpy(&old_sri, sri, sizeof(old_sri)); + error = scrub_call_kernel(ctx, xfdp, sri); if (error) - break; - } + return error; + } while (scrub_item_call_kernel_again_future(sri, SCRUB_ITEM_NEEDSCHECK, + &old_sri)); - return error; + return 0; } /* How many items do we have to check? */ @@ -562,3 +667,13 @@ can_force_rebuild( return __scrub_test(ctx, XFS_SCRUB_TYPE_PROBE, XFS_SCRUB_IFLAG_REPAIR | XFS_SCRUB_IFLAG_FORCE_REBUILD); } + +void +check_scrubv( + struct scrub_ctx *ctx) +{ + struct xfs_scrub_vec_head head = { }; + + /* We set the fallback flag if this doesn't work. */ + xfrog_scrubv_metadata(&ctx->mnt, &head); +} diff --git a/scrub/scrub.h b/scrub/scrub.h index 90578108a1c..183b89379cb 100644 --- a/scrub/scrub.h +++ b/scrub/scrub.h @@ -138,6 +138,8 @@ bool can_scrub_parent(struct scrub_ctx *ctx); bool can_repair(struct scrub_ctx *ctx); bool can_force_rebuild(struct scrub_ctx *ctx); +void check_scrubv(struct scrub_ctx *ctx); + int scrub_file(struct scrub_ctx *ctx, int fd, const struct xfs_bulkstat *bstat, unsigned int type, struct scrub_item *sri); diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h index 98a9238f2aa..ecdce680d81 100644 --- a/scrub/scrub_private.h +++ b/scrub/scrub_private.h @@ -8,6 +8,26 @@ /* Shared code between scrub.c and repair.c. */ +/* + * Declare a structure big enough to handle all scrub types + barriers, and + * an iteration pointer. So far we only need two barriers. + */ +struct scrubv_head { + struct xfs_scrub_vec_head head; + struct xfs_scrub_vec __vecs[XFS_SCRUB_TYPE_NR + 2]; + unsigned int i; +}; + +#define foreach_bighead_vec(bh, v) \ + for ((bh)->i = 0, (v) = (bh)->head.svh_vecs; \ + (bh)->i < (bh)->head.svh_nr; \ + (bh)->i++, (v)++) + +void scrub_item_to_vhead(struct scrubv_head *bighead, + const struct scrub_item *sri); +void scrub_vhead_add(struct scrubv_head *bighead, const struct scrub_item *sri, + unsigned int scrub_type); + int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen, void *where); @@ -104,5 +124,6 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type) bool scrub_item_call_kernel_again(struct scrub_item *sri, unsigned int scrub_type, uint8_t work_mask, const struct scrub_item *old); +bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags); #endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */ From patchwork Sun Dec 31 23:21:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508087 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 871CEC2C0 for ; Sun, 31 Dec 2023 23:21:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="S02Q24SF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 48113C433C7; Sun, 31 Dec 2023 23:21:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064907; bh=1W3MYOTtcC87+RAMtv/8IIJejPZkHEKQYNe9qR2rL+U=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=S02Q24SFo/OVl/R4zx8cHkZ7k9nTqWmYDHBluswDYE4m6eCurvKc38veCHTKNWjCX C/SiZe5RaKPVafyDCfWcazKMWnwZg44KNaEffTUTob9WrV+Wj5RKWfP8/Ecp3ikq/O L5lqA9u8XvqBPgEgfy2qHhhkEGbcuiomfFdaofuMt2nW4gkP21RBRzilpf00Fi2YuM 9DI8jizgTDSpvkdoZFBENlhNleXeln0cirtHdxf4QUVwJBR69xdfNRAhKdym+zRdit AncAudHnHW65wbThwNhf8KfQI3b1ysCPGiZbKMp4kd6gtNrfuVt1oCE30z3rRj7cm1 cC87xXxUKdikA== Date: Sun, 31 Dec 2023 15:21:46 -0800 Subject: [PATCH 08/10] xfs_scrub: vectorize repair calls From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007949.1806194.7189660898728830700.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Use the new vectorized scrub kernel calls to reduce the overhead of performing repairs. Signed-off-by: Darrick J. Wong --- scrub/repair.c | 267 +++++++++++++++++++++++++++---------------------- scrub/scrub.c | 79 +++----------- scrub/scrub_private.h | 7 + 3 files changed, 166 insertions(+), 187 deletions(-) diff --git a/scrub/repair.c b/scrub/repair.c index 7a710a159e6..a8e61255c5f 100644 --- a/scrub/repair.c +++ b/scrub/repair.c @@ -20,11 +20,6 @@ #include "descr.h" #include "scrub_private.h" -static int repair_epilogue(struct scrub_ctx *ctx, struct descr *dsc, - struct scrub_item *sri, unsigned int repair_flags, - const struct xfs_scrub_vec *oldm, - const struct xfs_scrub_vec *meta); - /* General repair routines. */ /* @@ -83,64 +78,14 @@ repair_want_service_downgrade( return false; } -/* Repair some metadata. */ -static int -xfs_repair_metadata( - struct scrub_ctx *ctx, - struct xfs_fd *xfdp, - unsigned int scrub_type, - struct scrub_item *sri, - unsigned int repair_flags) +static inline void +restore_oldvec( + struct xfs_scrub_vec *oldvec, + const struct scrub_item *sri, + unsigned int scrub_type) { - struct xfs_scrub_metadata meta = { 0 }; - struct xfs_scrub_vec oldm, vec; - DEFINE_DESCR(dsc, ctx, format_scrub_descr); - bool repair_only; - - /* - * If the caller boosted the priority of this scrub type on behalf of a - * higher level repair by setting IFLAG_REPAIR, turn off REPAIR_ONLY. - */ - repair_only = (repair_flags & XRM_REPAIR_ONLY) && - scrub_item_type_boosted(sri, scrub_type); - - assert(scrub_type < XFS_SCRUB_TYPE_NR); - assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); - meta.sm_type = scrub_type; - meta.sm_flags = XFS_SCRUB_IFLAG_REPAIR; - if (use_force_rebuild) - meta.sm_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD; - switch (xfrog_scrubbers[scrub_type].group) { - case XFROG_SCRUB_GROUP_AGHEADER: - case XFROG_SCRUB_GROUP_PERAG: - meta.sm_agno = sri->sri_agno; - break; - case XFROG_SCRUB_GROUP_INODE: - meta.sm_ino = sri->sri_ino; - meta.sm_gen = sri->sri_gen; - break; - default: - break; - } - - vec.sv_type = scrub_type; - vec.sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY; - memcpy(&oldm, &vec, sizeof(struct xfs_scrub_vec)); - if (!is_corrupt(&vec) && repair_only) - return 0; - - descr_set(&dsc, &meta); - - if (needs_repair(&vec)) - str_info(ctx, descr_render(&dsc), _("Attempting repair.")); - else if (debug || verbose) - str_info(ctx, descr_render(&dsc), - _("Attempting optimization.")); - - vec.sv_ret = xfrog_scrub_metadata(xfdp, &meta); - vec.sv_flags = meta.sm_flags; - - return repair_epilogue(ctx, &dsc, sri, repair_flags, &oldm, &vec); + oldvec->sv_type = scrub_type; + oldvec->sv_flags = sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY; } static int @@ -149,12 +94,15 @@ repair_epilogue( struct descr *dsc, struct scrub_item *sri, unsigned int repair_flags, - const struct xfs_scrub_vec *oldm, const struct xfs_scrub_vec *meta) { + struct xfs_scrub_vec oldv; + struct xfs_scrub_vec *oldm = &oldv; unsigned int scrub_type = meta->sv_type; int error = -meta->sv_ret; + restore_oldvec(oldm, sri, meta->sv_type); + switch (error) { case 0: /* No operational errors encountered. */ @@ -296,6 +244,132 @@ _("Repair unsuccessful; offline repair required.")); return 0; } +/* Decide if the dependent scrub types of the given scrub type are ok. */ +static bool +repair_item_dependencies_ok( + const struct scrub_item *sri, + unsigned int scrub_type) +{ + unsigned int dep_mask = repair_deps[scrub_type]; + unsigned int b; + + for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) { + if (!(dep_mask & 1)) + continue; + /* + * If this lower level object also needs repair, we can't fix + * the higher level item. + */ + if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR) + return false; + } + + return true; +} + +/* Decide if we want to repair a particular type of metadata. */ +static bool +can_repair_now( + const struct scrub_item *sri, + unsigned int scrub_type, + __u32 repair_mask, + unsigned int repair_flags) +{ + struct xfs_scrub_vec oldvec; + bool repair_only; + + /* Do we even need to repair this thing? */ + if (!(sri->sri_state[scrub_type] & repair_mask)) + return false; + + restore_oldvec(&oldvec, sri, scrub_type); + + /* + * If the caller boosted the priority of this scrub type on behalf of a + * higher level repair by setting IFLAG_REPAIR, ignore REPAIR_ONLY. + */ + repair_only = (repair_flags & XRM_REPAIR_ONLY) && + !(sri->sri_state[scrub_type] & SCRUB_ITEM_BOOST_REPAIR); + if (!is_corrupt(&oldvec) && repair_only) + return false; + + /* + * Don't try to repair higher level items if their lower-level + * dependencies haven't been verified, unless this is our last chance + * to fix things without complaint. + */ + if (!(repair_flags & XRM_FINAL_WARNING) && + !repair_item_dependencies_ok(sri, scrub_type)) + return false; + + return true; +} + +/* + * Repair some metadata. + * + * Returns 0 for success (or repair item deferral), or ECANCELED to abort the + * program. + */ +static int +repair_call_kernel( + struct scrub_ctx *ctx, + struct xfs_fd *xfdp, + struct scrub_item *sri, + __u32 repair_mask, + unsigned int repair_flags) +{ + DEFINE_DESCR(dsc, ctx, format_scrubv_descr); + struct scrubv_head bh = { }; + struct xfs_scrub_vec *v; + unsigned int scrub_type; + int error; + + assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); + + scrub_item_to_vhead(&bh, sri); + descr_set(&dsc, &bh); + + foreach_scrub_type(scrub_type) { + if (scrub_excessive_errors(ctx)) + return ECANCELED; + + if (!can_repair_now(sri, scrub_type, repair_mask, + repair_flags)) + continue; + + scrub_vhead_add(&bh, sri, scrub_type, true); + + if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR) + str_info(ctx, descr_render(&dsc), + _("Attempting repair.")); + else if (debug || verbose) + str_info(ctx, descr_render(&dsc), + _("Attempting optimization.")); + + dbg_printf("repair %s flags %xh tries %u\n", descr_render(&dsc), + sri->sri_state[scrub_type], + sri->sri_tries[scrub_type]); + } + + error = -xfrog_scrubv_metadata(xfdp, &bh.head); + if (error) + return error; + + foreach_bighead_vec(&bh, v) { + error = repair_epilogue(ctx, &dsc, sri, repair_flags, v); + if (error) + return error; + + /* Maybe update progress if we fixed the problem. */ + if (!(repair_flags & XRM_NOPROGRESS) && + !(sri->sri_state[v->sv_type] & SCRUB_ITEM_REPAIR_ANY)) + progress_add(1); + } + + return 0; +} + /* * Prioritize action items in order of how long we can wait. * @@ -632,29 +706,6 @@ action_list_process( return ret; } -/* Decide if the dependent scrub types of the given scrub type are ok. */ -static bool -repair_item_dependencies_ok( - const struct scrub_item *sri, - unsigned int scrub_type) -{ - unsigned int dep_mask = repair_deps[scrub_type]; - unsigned int b; - - for (b = 0; dep_mask && b < XFS_SCRUB_TYPE_NR; b++, dep_mask >>= 1) { - if (!(dep_mask & 1)) - continue; - /* - * If this lower level object also needs repair, we can't fix - * the higher level item. - */ - if (sri->sri_state[b] & SCRUB_ITEM_NEEDSREPAIR) - return false; - } - - return true; -} - /* * For a given filesystem object, perform all repairs of a given class * (corrupt, xcorrupt, xfail, preen) if the repair item says it's needed. @@ -670,13 +721,14 @@ repair_item_class( struct xfs_fd xfd; struct scrub_item old_sri; struct xfs_fd *xfdp = &ctx->mnt; - unsigned int scrub_type; int error = 0; if (ctx->mode == SCRUB_MODE_DRY_RUN) return 0; if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN)) return 0; + if (!scrub_item_schedule_work(sri, repair_mask)) + return 0; /* * If the caller passed us a file descriptor for a scrub, use it @@ -689,39 +741,14 @@ repair_item_class( xfdp = &xfd; } - foreach_scrub_type(scrub_type) { - if (scrub_excessive_errors(ctx)) - return ECANCELED; - - if (!(sri->sri_state[scrub_type] & repair_mask)) - continue; - - /* - * Don't try to repair higher level items if their lower-level - * dependencies haven't been verified, unless this is our last - * chance to fix things without complaint. - */ - if (!(flags & XRM_FINAL_WARNING) && - !repair_item_dependencies_ok(sri, scrub_type)) - continue; - - sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES; - do { - memcpy(&old_sri, sri, sizeof(old_sri)); - error = xfs_repair_metadata(ctx, xfdp, scrub_type, sri, - flags); - if (error) - return error; - } while (scrub_item_call_kernel_again(sri, scrub_type, - repair_mask, &old_sri)); - - /* Maybe update progress if we fixed the problem. */ - if (!(flags & XRM_NOPROGRESS) && - !(sri->sri_state[scrub_type] & SCRUB_ITEM_REPAIR_ANY)) - progress_add(1); - } - - return error; + do { + memcpy(&old_sri, sri, sizeof(struct scrub_item)); + error = repair_call_kernel(ctx, xfdp, sri, repair_mask, flags); + if (error) + return error; + } while (scrub_item_call_kernel_again(sri, repair_mask, &old_sri)); + + return 0; } /* diff --git a/scrub/scrub.c b/scrub/scrub.c index ec8d5e92cea..9f982eee701 100644 --- a/scrub/scrub.c +++ b/scrub/scrub.c @@ -25,7 +25,7 @@ /* Online scrub and repair wrappers. */ /* Describe the current state of a vectored scrub. */ -static int +int format_scrubv_descr( struct scrub_ctx *ctx, char *buf, @@ -55,35 +55,6 @@ format_scrubv_descr( return -1; } -/* Format a scrub description. */ -int -format_scrub_descr( - struct scrub_ctx *ctx, - char *buf, - size_t buflen, - void *where) -{ - struct xfs_scrub_metadata *meta = where; - const struct xfrog_scrub_descr *sc = &xfrog_scrubbers[meta->sm_type]; - - switch (sc->group) { - case XFROG_SCRUB_GROUP_AGHEADER: - case XFROG_SCRUB_GROUP_PERAG: - return snprintf(buf, buflen, _("AG %u %s"), meta->sm_agno, - _(sc->descr)); - case XFROG_SCRUB_GROUP_INODE: - return scrub_render_ino_descr(ctx, buf, buflen, - meta->sm_ino, meta->sm_gen, "%s", - _(sc->descr)); - case XFROG_SCRUB_GROUP_FS: - case XFROG_SCRUB_GROUP_SUMMARY: - case XFROG_SCRUB_GROUP_ISCAN: - case XFROG_SCRUB_GROUP_NONE: - return snprintf(buf, buflen, _("%s"), _(sc->descr)); - } - return -1; -} - /* Warn about strange circumstances after scrub. */ void scrub_warn_incomplete_scrub( @@ -262,13 +233,18 @@ void scrub_vhead_add( struct scrubv_head *bighead, const struct scrub_item *sri, - unsigned int scrub_type) + unsigned int scrub_type, + bool repair) { struct xfs_scrub_vec_head *vhead = &bighead->head; struct xfs_scrub_vec *v; v = &vhead->svh_vecs[vhead->svh_nr++]; v->sv_type = scrub_type; + if (repair) + v->sv_flags |= XFS_SCRUB_IFLAG_REPAIR; + if (repair && use_force_rebuild) + v->sv_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD; bighead->i = v - vhead->svh_vecs; } @@ -293,7 +269,7 @@ scrub_call_kernel( foreach_scrub_type(scrub_type) { if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK)) continue; - scrub_vhead_add(&bh, sri, scrub_type); + scrub_vhead_add(&bh, sri, scrub_type, false); dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc), sri->sri_state[scrub_type], @@ -357,8 +333,8 @@ scrub_item_schedule_group( } /* Decide if we call the kernel again to finish scrub/repair activity. */ -static inline bool -scrub_item_call_kernel_again_future( +bool +scrub_item_call_kernel_again( struct scrub_item *sri, uint8_t work_mask, const struct scrub_item *old) @@ -374,6 +350,11 @@ scrub_item_call_kernel_again_future( if (!nr) return false; + /* + * We are willing to go again if the last call had any effect on the + * state of the scrub item that the caller cares about or if the kernel + * asked us to try again. + */ foreach_scrub_type(scrub_type) { uint8_t statex = sri->sri_state[scrub_type] ^ old->sri_state[scrub_type]; @@ -387,34 +368,6 @@ scrub_item_call_kernel_again_future( return false; } -/* Decide if we call the kernel again to finish scrub/repair activity. */ -bool -scrub_item_call_kernel_again( - struct scrub_item *sri, - unsigned int scrub_type, - uint8_t work_mask, - const struct scrub_item *old) -{ - uint8_t statex; - - /* If there's nothing to do, we're done. */ - if (!(sri->sri_state[scrub_type] & work_mask)) - return false; - - /* - * We are willing to go again if the last call had any effect on the - * state of the scrub item that the caller cares about, if the freeze - * flag got set, or if the kernel asked us to try again... - */ - statex = sri->sri_state[scrub_type] ^ old->sri_state[scrub_type]; - if (statex & work_mask) - return true; - if (sri->sri_tries[scrub_type] != old->sri_tries[scrub_type]) - return true; - - return false; -} - /* * For each scrub item whose state matches the state_flags, set up the item * state for a kernel call. Returns true if any work was scheduled. @@ -469,7 +422,7 @@ scrub_item_check_file( error = scrub_call_kernel(ctx, xfdp, sri); if (error) return error; - } while (scrub_item_call_kernel_again_future(sri, SCRUB_ITEM_NEEDSCHECK, + } while (scrub_item_call_kernel_again(sri, SCRUB_ITEM_NEEDSCHECK, &old_sri)); return 0; diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h index ecdce680d81..ceda8ea1505 100644 --- a/scrub/scrub_private.h +++ b/scrub/scrub_private.h @@ -26,9 +26,9 @@ struct scrubv_head { void scrub_item_to_vhead(struct scrubv_head *bighead, const struct scrub_item *sri); void scrub_vhead_add(struct scrubv_head *bighead, const struct scrub_item *sri, - unsigned int scrub_type); + unsigned int scrub_type, bool repair); -int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen, +int format_scrubv_descr(struct scrub_ctx *ctx, char *buf, size_t buflen, void *where); /* Predicates for scrub flag state. */ @@ -121,8 +121,7 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type) return true; } -bool scrub_item_call_kernel_again(struct scrub_item *sri, - unsigned int scrub_type, uint8_t work_mask, +bool scrub_item_call_kernel_again(struct scrub_item *sri, uint8_t work_mask, const struct scrub_item *old); bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags); From patchwork Sun Dec 31 23:22:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508088 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9DA4FC2CC for ; Sun, 31 Dec 2023 23:22:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BNheJDL8" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F2A5EC433C8; Sun, 31 Dec 2023 23:22:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064923; bh=/C02IGY70ojP2czbtAy9C4sWPGvH7pk1kdjWyk81X/s=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=BNheJDL8pgwRH51E49bjD4f0PkSAEjLVRk5yCgghy+qPW0mb7WpVhg9MAj5JQIhV5 sU8sqWn8ItVCwObTXMLYL8xY7D5UUj6K0FmaH1G7JV9r7lQly+9saxSX/hIADb6bzZ 3j1lOH1qYGWY9LjOm0iQWrPSNHKg843lyEGMK260RsXk2QWC2hmx8HWwteh0YP1rtz u1Q2W/LGKylsiBePCwnTFVFPQR4HWlEQD1n38sLFA+PheLaC8cx8qfr/ckRTL+Nhts +vcgzSAlCS8M5gbwwcxSdQSLwj3fILJOu8mh47oi2DVbT5IvEEm2+U8YOhz9FR/9r4 oWIf/PbxNta8w== Date: Sun, 31 Dec 2023 15:22:02 -0800 Subject: [PATCH 09/10] xfs_scrub: use scrub barriers to reduce kernel calls From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007962.1806194.8270072828641970979.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Use scrub barriers so that we can submit a single scrub request for a bunch of things, and have the kernel stop midway through if it finds anything broken. Signed-off-by: Darrick J. Wong --- scrub/phase2.c | 15 ++------- scrub/phase3.c | 17 +--------- scrub/repair.c | 32 ++++++++++++++++++- scrub/scrub.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++- scrub/scrub.h | 17 ++++++++++ scrub/scrub_private.h | 4 ++ 6 files changed, 134 insertions(+), 32 deletions(-) diff --git a/scrub/phase2.c b/scrub/phase2.c index 57c6d0ef213..d435da07125 100644 --- a/scrub/phase2.c +++ b/scrub/phase2.c @@ -91,21 +91,12 @@ scan_ag_metadata( snprintf(descr, DESCR_BUFSZ, _("AG %u"), agno); /* - * First we scrub and fix the AG headers, because we need - * them to work well enough to check the AG btrees. + * First we scrub and fix the AG headers, because we need them to work + * well enough to check the AG btrees. Then scrub the AG btrees. */ scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_AGHEADER); - ret = scrub_item_check(ctx, &sri); - if (ret) - goto err; - - /* Repair header damage. */ - ret = repair_item_corruption(ctx, &sri); - if (ret) - goto err; - - /* Now scrub the AG btrees. */ scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_PERAG); + ret = scrub_item_check(ctx, &sri); if (ret) goto err; diff --git a/scrub/phase3.c b/scrub/phase3.c index 98e5c5a1f9f..09a1ea452bb 100644 --- a/scrub/phase3.c +++ b/scrub/phase3.c @@ -145,25 +145,11 @@ scrub_inode( /* Scrub the inode. */ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_INODE); - error = scrub_item_check_file(ctx, &sri, fd); - if (error) - goto out; - - error = try_inode_repair(ictx, &sri, fd); - if (error) - goto out; /* Scrub all block mappings. */ scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTD); scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTA); scrub_item_schedule(&sri, XFS_SCRUB_TYPE_BMBTC); - error = scrub_item_check_file(ctx, &sri, fd); - if (error) - goto out; - - error = try_inode_repair(ictx, &sri, fd); - if (error) - goto out; /* * Check file data contents, e.g. symlink and directory entries. @@ -182,11 +168,12 @@ scrub_inode( scrub_item_schedule(&sri, XFS_SCRUB_TYPE_XATTR); scrub_item_schedule(&sri, XFS_SCRUB_TYPE_PARENT); + + /* Try to check and repair the file while it's open. */ error = scrub_item_check_file(ctx, &sri, fd); if (error) goto out; - /* Try to repair the file while it's open. */ error = try_inode_repair(ictx, &sri, fd); if (error) goto out; diff --git a/scrub/repair.c b/scrub/repair.c index a8e61255c5f..1ce0283cb7f 100644 --- a/scrub/repair.c +++ b/scrub/repair.c @@ -323,6 +323,7 @@ repair_call_kernel( struct scrubv_head bh = { }; struct xfs_scrub_vec *v; unsigned int scrub_type; + bool need_barrier = false; int error; assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); @@ -338,6 +339,11 @@ repair_call_kernel( repair_flags)) continue; + if (need_barrier) { + scrub_vhead_add_barrier(&bh); + need_barrier = false; + } + scrub_vhead_add(&bh, sri, scrub_type, true); if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR) @@ -350,6 +356,17 @@ repair_call_kernel( dbg_printf("repair %s flags %xh tries %u\n", descr_render(&dsc), sri->sri_state[scrub_type], sri->sri_tries[scrub_type]); + + /* + * One of the other scrub types depends on this one. Set us up + * to add a repair barrier if we decide to schedule a repair + * after this one. If the UNFIXED flag is set, that means this + * is our last chance to fix things, so we skip the barriers + * just let everything run. + */ + if (!(repair_flags & XRM_FINAL_WARNING) && + (sri->sri_state[scrub_type] & SCRUB_ITEM_BARRIER)) + need_barrier = true; } error = -xfrog_scrubv_metadata(xfdp, &bh.head); @@ -357,6 +374,16 @@ repair_call_kernel( return error; foreach_bighead_vec(&bh, v) { + /* Deal with barriers separately. */ + if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) { + /* -ECANCELED means the kernel stopped here. */ + if (v->sv_ret == -ECANCELED) + return 0; + if (v->sv_ret) + return -v->sv_ret; + continue; + } + error = repair_epilogue(ctx, &dsc, sri, repair_flags, v); if (error) return error; @@ -445,7 +472,8 @@ repair_item_boost_priorities( * bits are left untouched to force a rescan in phase 4. */ #define MUSTFIX_STATES (SCRUB_ITEM_CORRUPT | \ - SCRUB_ITEM_BOOST_REPAIR) + SCRUB_ITEM_BOOST_REPAIR | \ + SCRUB_ITEM_BARRIER) /* * Figure out which AG metadata must be fixed before we can move on * to the inode scan. @@ -727,7 +755,7 @@ repair_item_class( return 0; if (ctx->mode == SCRUB_MODE_PREEN && !(repair_mask & SCRUB_ITEM_PREEN)) return 0; - if (!scrub_item_schedule_work(sri, repair_mask)) + if (!scrub_item_schedule_work(sri, repair_mask, repair_deps)) return 0; /* diff --git a/scrub/scrub.c b/scrub/scrub.c index 9f982eee701..2ec3cbc9aac 100644 --- a/scrub/scrub.c +++ b/scrub/scrub.c @@ -24,6 +24,35 @@ /* Online scrub and repair wrappers. */ +/* + * Bitmap showing the correctness dependencies between scrub types for scrubs. + * Dependencies cannot cross scrub groups. + */ +#define DEP(x) (1U << (x)) +static const unsigned int scrub_deps[XFS_SCRUB_TYPE_NR] = { + [XFS_SCRUB_TYPE_AGF] = DEP(XFS_SCRUB_TYPE_SB), + [XFS_SCRUB_TYPE_AGFL] = DEP(XFS_SCRUB_TYPE_SB) | + DEP(XFS_SCRUB_TYPE_AGF), + [XFS_SCRUB_TYPE_AGI] = DEP(XFS_SCRUB_TYPE_SB), + [XFS_SCRUB_TYPE_BNOBT] = DEP(XFS_SCRUB_TYPE_AGF), + [XFS_SCRUB_TYPE_CNTBT] = DEP(XFS_SCRUB_TYPE_AGF), + [XFS_SCRUB_TYPE_INOBT] = DEP(XFS_SCRUB_TYPE_AGI), + [XFS_SCRUB_TYPE_FINOBT] = DEP(XFS_SCRUB_TYPE_AGI), + [XFS_SCRUB_TYPE_RMAPBT] = DEP(XFS_SCRUB_TYPE_AGF), + [XFS_SCRUB_TYPE_REFCNTBT] = DEP(XFS_SCRUB_TYPE_AGF), + [XFS_SCRUB_TYPE_BMBTD] = DEP(XFS_SCRUB_TYPE_INODE), + [XFS_SCRUB_TYPE_BMBTA] = DEP(XFS_SCRUB_TYPE_INODE), + [XFS_SCRUB_TYPE_BMBTC] = DEP(XFS_SCRUB_TYPE_INODE), + [XFS_SCRUB_TYPE_DIR] = DEP(XFS_SCRUB_TYPE_BMBTD), + [XFS_SCRUB_TYPE_XATTR] = DEP(XFS_SCRUB_TYPE_BMBTA), + [XFS_SCRUB_TYPE_SYMLINK] = DEP(XFS_SCRUB_TYPE_BMBTD), + [XFS_SCRUB_TYPE_PARENT] = DEP(XFS_SCRUB_TYPE_BMBTD), + [XFS_SCRUB_TYPE_QUOTACHECK] = DEP(XFS_SCRUB_TYPE_UQUOTA) | + DEP(XFS_SCRUB_TYPE_GQUOTA) | + DEP(XFS_SCRUB_TYPE_PQUOTA), +}; +#undef DEP + /* Describe the current state of a vectored scrub. */ int format_scrubv_descr( @@ -248,6 +277,21 @@ scrub_vhead_add( bighead->i = v - vhead->svh_vecs; } +/* Add a barrier to the scrub vector. */ +void +scrub_vhead_add_barrier( + struct scrubv_head *bighead) +{ + struct xfs_scrub_vec_head *vhead = &bighead->head; + struct xfs_scrub_vec *v; + + v = &vhead->svh_vecs[vhead->svh_nr++]; + v->sv_type = XFS_SCRUB_TYPE_BARRIER; + v->sv_flags = XFS_SCRUB_OFLAG_CORRUPT | XFS_SCRUB_OFLAG_XFAIL | + XFS_SCRUB_OFLAG_XCORRUPT | XFS_SCRUB_OFLAG_INCOMPLETE; + bighead->i = v - vhead->svh_vecs; +} + /* Do a read-only check of some metadata. */ static int scrub_call_kernel( @@ -259,6 +303,7 @@ scrub_call_kernel( struct scrubv_head bh = { }; struct xfs_scrub_vec *v; unsigned int scrub_type; + bool need_barrier = false; int error; assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); @@ -269,8 +314,17 @@ scrub_call_kernel( foreach_scrub_type(scrub_type) { if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK)) continue; + + if (need_barrier) { + scrub_vhead_add_barrier(&bh); + need_barrier = false; + } + scrub_vhead_add(&bh, sri, scrub_type, false); + if (sri->sri_state[scrub_type] & SCRUB_ITEM_BARRIER) + need_barrier = true; + dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc), sri->sri_state[scrub_type], sri->sri_tries[scrub_type]); @@ -281,6 +335,16 @@ scrub_call_kernel( return error; foreach_bighead_vec(&bh, v) { + /* Deal with barriers separately. */ + if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) { + /* -ECANCELED means the kernel stopped here. */ + if (v->sv_ret == -ECANCELED) + return 0; + if (v->sv_ret) + return -v->sv_ret; + continue; + } + error = scrub_epilogue(ctx, &dsc, sri, v); if (error) return error; @@ -375,15 +439,25 @@ scrub_item_call_kernel_again( bool scrub_item_schedule_work( struct scrub_item *sri, - uint8_t state_flags) + uint8_t state_flags, + const unsigned int *schedule_deps) { unsigned int scrub_type; unsigned int nr = 0; foreach_scrub_type(scrub_type) { + unsigned int j; + + sri->sri_state[scrub_type] &= ~SCRUB_ITEM_BARRIER; + if (!(sri->sri_state[scrub_type] & state_flags)) continue; + foreach_scrub_type(j) { + if (schedule_deps[scrub_type] & (1U << j)) + sri->sri_state[j] |= SCRUB_ITEM_BARRIER; + } + sri->sri_tries[scrub_type] = SCRUB_ITEM_MAX_RETRIES; nr++; } @@ -403,7 +477,7 @@ scrub_item_check_file( struct xfs_fd *xfdp = &ctx->mnt; int error = 0; - if (!scrub_item_schedule_work(sri, SCRUB_ITEM_NEEDSCHECK)) + if (!scrub_item_schedule_work(sri, SCRUB_ITEM_NEEDSCHECK, scrub_deps)) return 0; /* @@ -627,6 +701,9 @@ check_scrubv( { struct xfs_scrub_vec_head head = { }; + if (debug_tweak_on("XFS_SCRUB_FORCE_SINGLE")) + ctx->mnt.flags |= XFROG_FLAG_SCRUB_FORCE_SINGLE; + /* We set the fallback flag if this doesn't work. */ xfrog_scrubv_metadata(&ctx->mnt, &head); } diff --git a/scrub/scrub.h b/scrub/scrub.h index 183b89379cb..c3eed1b261d 100644 --- a/scrub/scrub.h +++ b/scrub/scrub.h @@ -30,6 +30,9 @@ enum xfrog_scrub_group; /* This scrub type needs to be checked. */ #define SCRUB_ITEM_NEEDSCHECK (1 << 5) +/* Scrub barrier. */ +#define SCRUB_ITEM_BARRIER (1 << 6) + /* All of the state flags that we need to prioritize repair work. */ #define SCRUB_ITEM_REPAIR_ANY (SCRUB_ITEM_CORRUPT | \ SCRUB_ITEM_PREEN | \ @@ -126,6 +129,20 @@ scrub_item_check(struct scrub_ctx *ctx, struct scrub_item *sri) return scrub_item_check_file(ctx, sri, -1); } +/* Count the number of metadata objects still needing a scrub. */ +static inline unsigned int +scrub_item_count_needscheck( + const struct scrub_item *sri) +{ + unsigned int ret = 0; + unsigned int i; + + foreach_scrub_type(i) + if (sri->sri_state[i] & SCRUB_ITEM_NEEDSCHECK) + ret++; + return ret; +} + void scrub_report_preen_triggers(struct scrub_ctx *ctx); bool can_scrub_fs_metadata(struct scrub_ctx *ctx); diff --git a/scrub/scrub_private.h b/scrub/scrub_private.h index ceda8ea1505..5014feee515 100644 --- a/scrub/scrub_private.h +++ b/scrub/scrub_private.h @@ -27,6 +27,7 @@ void scrub_item_to_vhead(struct scrubv_head *bighead, const struct scrub_item *sri); void scrub_vhead_add(struct scrubv_head *bighead, const struct scrub_item *sri, unsigned int scrub_type, bool repair); +void scrub_vhead_add_barrier(struct scrubv_head *bighead); int format_scrubv_descr(struct scrub_ctx *ctx, char *buf, size_t buflen, void *where); @@ -123,6 +124,7 @@ scrub_item_schedule_retry(struct scrub_item *sri, unsigned int scrub_type) bool scrub_item_call_kernel_again(struct scrub_item *sri, uint8_t work_mask, const struct scrub_item *old); -bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags); +bool scrub_item_schedule_work(struct scrub_item *sri, uint8_t state_flags, + const unsigned int *schedule_deps); #endif /* XFS_SCRUB_SCRUB_PRIVATE_H_ */ From patchwork Sun Dec 31 23:22:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13508089 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C0BAEC2C0 for ; Sun, 31 Dec 2023 23:22:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="vNX5AiQS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 930E5C433C7; Sun, 31 Dec 2023 23:22:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1704064938; bh=HNPSLdnNtHz9GJXQzMX8HxLYqrcLBTCz5nv6bhh0tpk=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=vNX5AiQSTMWuC7piWU7IlmR1RVvBPRcNNZfoGrd0q5j1dd/djX7w8LtMGty3sBVJ+ gKU88tyAlHEFSHJ2uhWOUotQLiJmOrUATvQmtnCYAwb35VqpHlwctPNeEcs38lQzDd 9D3Ilh2urhHPQf9nmEYj5Z/LtHE1XxhVI7v+CJY+pvAys0GnfZ5UBSMjb4KdWOQ4Ua bI/xxSeXdrRYezo+zCnz4VLNi+Ib5xSJhEW/yxPR5ItoNThsMPQKtfkhpUxHubaAx0 4dHaaBUCsVDGkCTh19+777iBkDr35pr4f28TmUfH1WGRPQPRjUU5TKF0WykxzWwTiz j4OskFI1tzWJg== Date: Sun, 31 Dec 2023 15:22:18 -0800 Subject: [PATCH 10/10] xfs_scrub: try spot repairs of metadata items to make scrub progress From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org Message-ID: <170405007976.1806194.11727403919106349460.stgit@frogsfrogsfrogs> In-Reply-To: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> References: <170405007836.1806194.11810681886556560484.stgit@frogsfrogsfrogs> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-xfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Darrick J. Wong Now that we've enabled scrub dependency barriers, it's possible that a scrub_item_check call will return with some of the scrub items still in NEEDSCHECK state. If, for example, scrub type B depends on scrub type A being clean and A is not clean, B will still be in NEEDSCHECK state. In order to make as much scanning progress as possible during phase 2 and phase 3, allow ourselves to try some spot repairs in the hopes that it will enable us to make progress towards at least scanning the whole metadata item. If we can't make any forward progress, we'll queue the scrub item for repair in phase 4, which means that anything still in in NEEDSCHECK state becomes CORRUPT state. (At worst, the NEEDSCHECK item will actually be clean by phase 4, and xfs_scrub will report that it didn't need any work after all.) Signed-off-by: Darrick J. Wong --- scrub/phase2.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ scrub/phase3.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++- scrub/repair.c | 15 +++++++++++ 3 files changed, 163 insertions(+), 1 deletion(-) diff --git a/scrub/phase2.c b/scrub/phase2.c index d435da07125..c24d137358c 100644 --- a/scrub/phase2.c +++ b/scrub/phase2.c @@ -69,6 +69,53 @@ defer_fs_repair( return 0; } +/* + * If we couldn't check all the scheduled metadata items, try performing spot + * repairs until we check everything or stop making forward progress. + */ +static int +repair_and_scrub_loop( + struct scrub_ctx *ctx, + struct scrub_item *sri, + const char *descr, + bool *defer) +{ + unsigned int to_check; + int ret; + + *defer = false; + if (ctx->mode != SCRUB_MODE_REPAIR) + return 0; + + to_check = scrub_item_count_needscheck(sri); + while (to_check > 0) { + unsigned int nr; + + ret = repair_item_corruption(ctx, sri); + if (ret) + return ret; + + ret = scrub_item_check(ctx, sri); + if (ret) + return ret; + + nr = scrub_item_count_needscheck(sri); + if (nr == to_check) { + /* + * We cannot make forward scanning progress with this + * metadata, so defer the rest until phase 4. + */ + str_info(ctx, descr, + _("Unable to make forward checking progress; will try again in phase 4.")); + *defer = true; + return 0; + } + to_check = nr; + } + + return 0; +} + /* Scrub each AG's metadata btrees. */ static void scan_ag_metadata( @@ -82,6 +129,7 @@ scan_ag_metadata( struct scan_ctl *sctl = arg; char descr[DESCR_BUFSZ]; unsigned int difficulty; + bool defer_repairs; int ret; if (sctl->aborted) @@ -97,10 +145,22 @@ scan_ag_metadata( scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_AGHEADER); scrub_item_schedule_group(&sri, XFROG_SCRUB_GROUP_PERAG); + /* + * Try to check all of the AG metadata items that we just scheduled. + * If we return with some types still needing a check, try repairing + * any damaged metadata that we've found so far, and try again. Abort + * if we stop making forward progress. + */ ret = scrub_item_check(ctx, &sri); if (ret) goto err; + ret = repair_and_scrub_loop(ctx, &sri, descr, &defer_repairs); + if (ret) + goto err; + if (defer_repairs) + goto defer; + /* * Figure out if we need to perform early fixing. The only * reason we need to do this is if the inobt is broken, which @@ -117,6 +177,7 @@ scan_ag_metadata( if (ret) goto err; +defer: /* Everything else gets fixed during phase 4. */ ret = defer_fs_repair(ctx, &sri); if (ret) @@ -137,11 +198,18 @@ scan_fs_metadata( struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx; struct scan_ctl *sctl = arg; unsigned int difficulty; + bool defer_repairs; int ret; if (sctl->aborted) goto out; + /* + * Try to check all of the metadata files that we just scheduled. If + * we return with some types still needing a check, try repairing any + * damaged metadata that we've found so far, and try again. Abort if + * we stop making forward progress. + */ scrub_item_init_fs(&sri); scrub_item_schedule(&sri, type); ret = scrub_item_check(ctx, &sri); @@ -150,10 +218,20 @@ scan_fs_metadata( goto out; } + ret = repair_and_scrub_loop(ctx, &sri, xfrog_scrubbers[type].descr, + &defer_repairs); + if (ret) { + sctl->aborted = true; + goto out; + } + if (defer_repairs) + goto defer; + /* Complain about metadata corruptions that might not be fixable. */ difficulty = repair_item_difficulty(&sri); warn_repair_difficulties(ctx, difficulty, xfrog_scrubbers[type].descr); +defer: ret = defer_fs_repair(ctx, &sri); if (ret) { sctl->aborted = true; diff --git a/scrub/phase3.c b/scrub/phase3.c index 09a1ea452bb..046a42c1da8 100644 --- a/scrub/phase3.c +++ b/scrub/phase3.c @@ -99,6 +99,58 @@ try_inode_repair( return repair_file_corruption(ictx->ctx, sri, fd); } +/* + * If we couldn't check all the scheduled file metadata items, try performing + * spot repairs until we check everything or stop making forward progress. + */ +static int +repair_and_scrub_inode_loop( + struct scrub_ctx *ctx, + struct xfs_bulkstat *bstat, + int fd, + struct scrub_item *sri, + bool *defer) +{ + unsigned int to_check; + int error; + + *defer = false; + if (ctx->mode != SCRUB_MODE_REPAIR) + return 0; + + to_check = scrub_item_count_needscheck(sri); + while (to_check > 0) { + unsigned int nr; + + error = repair_file_corruption(ctx, sri, fd); + if (error) + return error; + + error = scrub_item_check_file(ctx, sri, fd); + if (error) + return error; + + nr = scrub_item_count_needscheck(sri); + if (nr == to_check) { + char descr[DESCR_BUFSZ]; + + /* + * We cannot make forward scanning progress with this + * inode, so defer the rest until phase 4. + */ + scrub_render_ino_descr(ctx, descr, DESCR_BUFSZ, + bstat->bs_ino, bstat->bs_gen, NULL); + str_info(ctx, descr, + _("Unable to make forward checking progress; will try again in phase 4.")); + *defer = true; + return 0; + } + to_check = nr; + } + + return 0; +} + /* Verify the contents, xattrs, and extent maps of an inode. */ static int scrub_inode( @@ -169,11 +221,28 @@ scrub_inode( scrub_item_schedule(&sri, XFS_SCRUB_TYPE_XATTR); scrub_item_schedule(&sri, XFS_SCRUB_TYPE_PARENT); - /* Try to check and repair the file while it's open. */ + /* + * Try to check all of the metadata items that we just scheduled. If + * we return with some types still needing a check and the space + * metadata isn't also in need of repairs, try repairing any damaged + * file metadata that we've found so far, and try checking the file + * again. Worst case, defer the repairs and the checks to phase 4 if + * we can't make any progress on anything. + */ error = scrub_item_check_file(ctx, &sri, fd); if (error) goto out; + if (!ictx->always_defer_repairs) { + bool defer_repairs; + + error = repair_and_scrub_inode_loop(ctx, bstat, fd, &sri, + &defer_repairs); + if (error || defer_repairs) + goto out; + } + + /* Try to repair the file while it's open. */ error = try_inode_repair(ictx, &sri, fd); if (error) goto out; diff --git a/scrub/repair.c b/scrub/repair.c index 1ce0283cb7f..7e131001e13 100644 --- a/scrub/repair.c +++ b/scrub/repair.c @@ -859,6 +859,7 @@ repair_item_to_action_item( struct action_item **aitemp) { struct action_item *aitem; + unsigned int scrub_type; if (repair_item_count_needsrepair(sri) == 0) return 0; @@ -874,6 +875,20 @@ repair_item_to_action_item( INIT_LIST_HEAD(&aitem->list); memcpy(&aitem->sri, sri, sizeof(struct scrub_item)); + /* + * If the scrub item indicates that there is unchecked metadata, assume + * that the scrub type checker depends on something that couldn't be + * fixed. Mark that type as corrupt so that phase 4 will try it again. + */ + foreach_scrub_type(scrub_type) { + __u8 *state = aitem->sri.sri_state; + + if (state[scrub_type] & SCRUB_ITEM_NEEDSCHECK) { + state[scrub_type] &= ~SCRUB_ITEM_NEEDSCHECK; + state[scrub_type] |= SCRUB_ITEM_CORRUPT; + } + } + *aitemp = aitem; return 0; }