From patchwork Tue Jul 2 01:22:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718874 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 7158D4C8B for ; Tue, 2 Jul 2024 01:22:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883330; cv=none; b=UWwaoa8n6YKAobylI4ZtykPy2xoAu3nsleG8DJcUaJYZpXfF6s/G3wrGHi5/5fVtVhJLlaXV58/59yQq9+myJBrisK1xGuUo1GFuAke8zXhx+dEtOnf/BIqyZypncIDAagpr1aWokTZDdFESuSVebapaxvHmPii3MQYktXnBrjE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883330; c=relaxed/simple; bh=ZO+JaNXEBq+GFNbfyYmu4DcvX4GvCZvAErILJiLrQJ4=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=bQhvF7L8owRhAnIFpb0Jr3HQny3sPslXfnAoJp3c94vE4pSeg866bC1MDupl3HZzKNUnPpbNxFgVekDDXf8wExMC3LTeusQhdENp/xLW/aCXxH4fbZoWaVkXAJXsi3DIcZrymhsYTIxJE61TENW76+E365NHQ/EGWmEJRqa39qs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ObpPNq4w; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ObpPNq4w" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3612EC116B1; Tue, 2 Jul 2024 01:22:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883330; bh=ZO+JaNXEBq+GFNbfyYmu4DcvX4GvCZvAErILJiLrQJ4=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=ObpPNq4wzubzklpm1Cy2wW6fwN+LU6aWFwpY1cRSPMTOwcN2oucNyNmnNb7lMC3HC I9d4XBKJ2TNp3zERCBIWz6hfm3+hOtb3kcdtz7XD5Z9wvLmLsl9R9jEAVZWTlBORF/ +C1QeqUzce9Lx475mwGIUN0UdwadhZH/Nx+s3XOGJ3AicTv9V/pa0FYK2/6tyNCPde RfebMW02B0K+JMKDhHdj1DoKcbODQDl50uwvPZ5dbqo4xk+bQeOkjxai/KdF6s1xMN Uy03onGdDwrci58YV7Ve6jvByolpByv1EeWucMnKz7Xay8XP5mD7BZbDGfTJ5hrKv2 OoC7SMzMRADJA== Date: Mon, 01 Jul 2024 18:22:09 -0700 Subject: [PATCH 01/10] man: document vectored scrub mode From: "Darrick J. Wong" To: djwong@kernel.org, cem@kernel.org Cc: linux-xfs@vger.kernel.org, hch@lst.de Message-ID: <171988123150.2012546.15521264443878817076.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Add a manpage to document XFS_IOC_SCRUBV_METADATA. From the kernel patch: Introduce a variant on XFS_SCRUB_METADATA that allows for a vectored mode. The caller specifies the principal metadata object that they want to scrub (allocation group, inode, etc.) once, followed by an array of scrub types they want called on that object. The kernel runs the scrub operations and writes the output flags and errno code to the corresponding array element. A new pseudo scrub type BARRIER is introduced to force the kernel to return to userspace if any corruptions have been found when scrubbing the previous scrub types in the array. This enables userspace to schedule, for example, the sequence: 1. data fork 2. barrier 3. directory If the data fork scrub is clean, then the kernel will perform the directory scrub. If not, the barrier in 2 will exit back to userspace. The alternative would have been an interface where userspace passes a pointer to an empty buffer, and the kernel formats that with xfs_scrub_vecs that tell userspace what it scrubbed and what the outcome was. With that the kernel would have to communicate that the buffer needed to have been at least X size, even though for our cases XFS_SCRUB_TYPE_NR + 2 would always be enough. Compared to that, this design keeps all the dependency policy and ordering logic in userspace where it already resides instead of duplicating it in the kernel. The downside of that is that it needs the barrier logic. When running fstests in "rebuild all metadata after each test" mode, I observed a 10% reduction in runtime due to fewer transitions across the system call boundary. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig --- man/man2/ioctl_xfs_scrubv_metadata.2 | 171 ++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 man/man2/ioctl_xfs_scrubv_metadata.2 diff --git a/man/man2/ioctl_xfs_scrubv_metadata.2 b/man/man2/ioctl_xfs_scrubv_metadata.2 new file mode 100644 index 000000000000..532916756df5 --- /dev/null +++ b/man/man2/ioctl_xfs_scrubv_metadata.2 @@ -0,0 +1,171 @@ +.\" 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 2024-05-21 "XFS" +.SH NAME +ioctl_xfs_scrubv_metadata \- check a lot of XFS filesystem metadata +.SH SYNOPSIS +.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; + __u64 svh_vectors; +}; +.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_vectors +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_vectors +array. + +The field +.I svh_vectors +is a pointer to an array of +.B struct xfs_scrub_vec +structures. + +.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 Tue Jul 2 01:22:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718875 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 10A006FC3 for ; Tue, 2 Jul 2024 01:22:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883346; cv=none; b=A2XITWwfkuWisz9YjElulycvo0sk5lE64KwVw9PEbGAvnYwgE1Lfq1fyh78SXDr3PJK0u/Zrul7W07dYK0U7HYLGLbphsv5l0aMXCMY9AUcQH+eLrP4Z5hbFpxB9/PZMa2KE/rBrKmDnhwcSYrCU0J0ROm/mbZzhS6Omv9yhhMk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883346; c=relaxed/simple; bh=JqhNY5zCz1lt4bDuwrcKw9CJ3OZjA67qjCRBgnqC4VY=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=d4X2e5JWoaOl2enlDoEuCxUHJz0TmPAL8sKa/FHqPC/TzKQRKmeq59O5CtrOtl4Vv/zPu8OEvJ/mF6d65BFuxRjp92XuUQINhPndN3IAj0Bjmm0H1URq25QLPPNYEWYQTj74Wb4EaEJMwRQZGeovNrJePcuTW7dulUempQiuPiU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=K4jdeRBd; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="K4jdeRBd" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D246CC116B1; Tue, 2 Jul 2024 01:22:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883345; bh=JqhNY5zCz1lt4bDuwrcKw9CJ3OZjA67qjCRBgnqC4VY=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=K4jdeRBdPfORHoIJNWWfapD0mQIImDQLgMwAYg0L/gIXaBxrm6mn52O+98hxZw0M+ P5HAj5LHcIGy7QMxoWZ9E6KPStdjTU5VyrHuOmZAKk0ysZSUUWVtERq2wD4c/auxbh oFYj0XOGvbIrJ805HpHdf7ZWgLgtNztaR0Ge+rTLFmgKbbVgFt/jsjyAZD5QsAelJY VqCTPLlMW1NXUrelRTy+IMCg7k7PCYf/QZCRW/KOYquGXmd/Xi7zWT0VnaKwxRMH6P 9SZ7/ileD0IVE1aIhjMD9DCZ9+QlY5OXqIYokylSfKGNBW9qg1rYqygokrkfq79wgp wsUTs8zRRv4mw== Date: Mon, 01 Jul 2024 18:22:25 -0700 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, hch@lst.de Message-ID: <171988123165.2012546.18193316836899391961.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- libfrog/fsgeom.h | 6 ++ libfrog/scrub.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ libfrog/scrub.h | 35 ++++++++++++++ 3 files changed, 178 insertions(+) diff --git a/libfrog/fsgeom.h b/libfrog/fsgeom.h index f327dc7d70d0..df2ca2a408e7 100644 --- a/libfrog/fsgeom.h +++ b/libfrog/fsgeom.h @@ -50,6 +50,12 @@ struct xfs_fd { /* Only use v5 bulkstat/inumbers ioctls. */ #define XFROG_FLAG_BULKSTAT_FORCE_V5 (1 << 1) +/* Only use the older one-at-a-time scrub ioctl. */ +#define XFROG_FLAG_SCRUB_FORCE_SINGLE (1 << 2) + +/* Only use the vectored scrub ioctl. */ +#define XFROG_FLAG_SCRUB_FORCE_VECTOR (1 << 3) + /* 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 a2146e228f5b..e233c0f9c8e1 100644 --- a/libfrog/scrub.c +++ b/libfrog/scrub.c @@ -171,3 +171,140 @@ xfrog_scrub_metadata( return 0; } + +/* Decide if there have been any scrub failures up to this point. */ +static inline int +xfrog_scrubv_check_barrier( + const struct xfs_scrub_vec *vectors, + const struct xfs_scrub_vec *stop_vec) +{ + const struct xfs_scrub_vec *v; + __u32 failmask; + + failmask = stop_vec->sv_flags & XFS_SCRUB_FLAGS_OUT; + + for (v = vectors; v < stop_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. + */ + switch (v->sv_ret) { + case -EBUSY: + case -ENOENT: + case -EUSERS: + case 0: + break; + default: + 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 xfrog_scrubv *scrubv) +{ + struct xfs_scrub_vec *vectors = scrubv->vectors; + struct xfs_scrub_vec *v; + unsigned int i; + + if (scrubv->head.svh_flags & ~XFS_SCRUB_VEC_FLAGS_ALL) + return -EINVAL; + + foreach_xfrog_scrubv_vec(scrubv, 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. */ + foreach_xfrog_scrubv_vec(scrubv, i, v) { + struct xfs_scrub_metadata sm = { + .sm_type = v->sv_type, + .sm_flags = v->sv_flags, + .sm_ino = scrubv->head.svh_ino, + .sm_gen = scrubv->head.svh_gen, + .sm_agno = scrubv->head.svh_agno, + }; + struct timespec tv; + + if (v->sv_type == XFS_SCRUB_TYPE_BARRIER) { + v->sv_ret = xfrog_scrubv_check_barrier(vectors, v); + if (v->sv_ret) + break; + continue; + } + + v->sv_ret = xfrog_scrub_metadata(xfd, &sm); + v->sv_flags = sm.sm_flags; + + if (scrubv->head.svh_rest_us) { + tv.tv_sec = 0; + tv.tv_nsec = scrubv->head.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 xfrog_scrubv *scrubv) +{ + int error = 0; + + if (scrubv->head.svh_nr > XFROG_SCRUBV_MAX_VECTORS) + return -EINVAL; + + if (xfd->flags & XFROG_FLAG_SCRUB_FORCE_SINGLE) + goto try_single; + + error = xfrog_scrubv_call(xfd, &scrubv->head); + 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, scrubv); +} diff --git a/libfrog/scrub.h b/libfrog/scrub.h index 27230c62f71a..b564c0d7bd0f 100644 --- a/libfrog/scrub.h +++ b/libfrog/scrub.h @@ -28,4 +28,39 @@ 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); +/* + * Allow enough space to call all scrub types with a barrier between each. + * This is overkill for every caller in xfsprogs. + */ +#define XFROG_SCRUBV_MAX_VECTORS (XFS_SCRUB_TYPE_NR * 2) + +struct xfrog_scrubv { + struct xfs_scrub_vec_head head; + struct xfs_scrub_vec vectors[XFROG_SCRUBV_MAX_VECTORS]; +}; + +/* Initialize a scrubv structure; callers must have zeroed @scrubv. */ +static inline void +xfrog_scrubv_init(struct xfrog_scrubv *scrubv) +{ + scrubv->head.svh_vectors = (uintptr_t)scrubv->vectors; +} + +/* Return the next free vector from the scrubv structure. */ +static inline struct xfs_scrub_vec * +xfrog_scrubv_next_vector(struct xfrog_scrubv *scrubv) +{ + if (scrubv->head.svh_nr >= XFROG_SCRUBV_MAX_VECTORS) + return NULL; + + return &scrubv->vectors[scrubv->head.svh_nr++]; +} + +#define foreach_xfrog_scrubv_vec(scrubv, i, vec) \ + for ((i) = 0, (vec) = (scrubv)->vectors; \ + (i) < (scrubv)->head.svh_nr; \ + (i)++, (vec)++) + +int xfrog_scrubv_metadata(struct xfs_fd *xfd, struct xfrog_scrubv *scrubv); + #endif /* __LIBFROG_SCRUB_H__ */ From patchwork Tue Jul 2 01:22:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718876 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 A50308489 for ; Tue, 2 Jul 2024 01:22:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883361; cv=none; b=aZXEpTrGc3CsW/9V8rKU99aHm14TW2qG+53woZeazhcG60EJNTctfAuL9foiWBESlHdEiktxiQAEFxuBHCsxsz4ec3R4Akr3ks7HMUiXCs9cGXMaxHkplqTApxPXIPdfSeiyukdytVHohcIsOcDYI30x8bYV58g1HQalubaFG0o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883361; c=relaxed/simple; bh=Ss2TmnWPh4d9EEZ6gav+QJnL51VHU4asi1b1xpPe3CY=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JPHwipVI9TlphWwfhkGwZF88N1c1WpmAxIBbQ4c37uofAWHZxYZZpxD5RE6LcFw0EyUQYhpvA7s/1sJY72G9ZGJniH0p2BsJdCBu0AF8HrAadYPloGMVyiw6bdPGf+qyQ5DAgnAlgLRTb6twuzm/rHbVe0rFi3Ru72EFap72l2M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ete/EC+c; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ete/EC+c" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 72BE2C116B1; Tue, 2 Jul 2024 01:22:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883361; bh=Ss2TmnWPh4d9EEZ6gav+QJnL51VHU4asi1b1xpPe3CY=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=Ete/EC+c/Lx28P/2cQmwxleVYvzIxoYT17ekQmFosVHcLkmV73xuEtMuXaAqbNVy5 bjqSaqL70tS28GnF0HRtIzYTczeknIn0XlAH/l5aJPVRvDeQyDJNKgTPwdkphzTNVs Gxx1jwPtKk0lGeAaFUtHYnfRuO28t62pwUHRbBGM4GjvtVlZvpkhkQCx+CBjO+Ks+G eGXB/fsBhFzoYJEoEfVJp+0v6Qnvhk5MfaAcHoyiPSCC2jofcTtz6Wvmh+TEGnOHk1 NOUbBFFVEA//HqmY7l47Kkk7dWjKCYoOijMtdfrpAUQdBy1avEn0hQMHJOT01Q/RiN lnFjkNg8iu5CA== Date: Mon, 01 Jul 2024 18:22:41 -0700 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, hch@lst.de Message-ID: <171988123180.2012546.8535944367157322296.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- io/scrub.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++------ man/man8/xfs_io.8 | 51 +++++++ 2 files changed, 379 insertions(+), 40 deletions(-) diff --git a/io/scrub.c b/io/scrub.c index a77cd872fede..e03c1d0eaf1d 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,273 @@ 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 xfrog_scrubv *scrubv, + int barrier_interval, + __u32 barrier_mask, + enum xfrog_scrub_group group) +{ + const struct xfrog_scrub_descr *d; + struct xfs_scrub_vec *v; + unsigned int i; + + for (i = 0, d = xfrog_scrubbers; i < XFS_SCRUB_TYPE_NR; i++, d++) { + if (d->group != group) + continue; + + v = xfrog_scrubv_next_vector(scrubv); + v->sv_type = i; + + if (barrier_interval && + scrubv->head.svh_nr % (barrier_interval + 1) == 0) { + v = xfrog_scrubv_next_vector(scrubv); + v->sv_flags = barrier_mask; + v->sv_type = XFS_SCRUB_TYPE_BARRIER; + } + } +} + +static int +scrubv_f( + int argc, + char **argv) +{ + struct xfrog_scrubv scrubv = { }; + struct xfs_fd xfd = XFS_FD_INIT(file->fd); + 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; + + xfrog_scrubv_init(&scrubv); + + 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; + } + + scrubv.head.svh_rest_us = rest_us; + foreach_xfrog_scrubv_vec(&scrubv, 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, &scrubv.head.svh_ino, + &scrubv.head.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, &scrubv.head.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(&scrubv, barrier_interval, barrier_mask, group); + assert(scrubv.head.svh_nr <= XFROG_SCRUBV_MAX_VECTORS); + + 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, &scrubv); + if (error) { + xfrog_perror(error, "xfrog_scrub_many"); + exitcode = 1; + return 0; + } + + /* Dump what happened. */ + if (debug) { + foreach_xfrog_scrubv_vec(&scrubv, 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. */ + foreach_xfrog_scrubv_vec(&scrubv, 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 02036e3d093b..657bdaec4381 100644 --- a/man/man8/xfs_io.8 +++ b/man/man8/xfs_io.8 @@ -1392,6 +1392,57 @@ inode number and generation number are specified. .RE .PD .TP +.BI "scrubv [ \-b NN ] [ \-d ] [ \-m oflags ] [ \-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 \-m +Fail scrub barriers if any of these scrub output flags are seen. +.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 Tue Jul 2 01:22:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718877 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 A5A798BFA for ; Tue, 2 Jul 2024 01:22:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883377; cv=none; b=EqcBOVLVxIk5i/emDctyHXLZkGLRfS9isesJJMdoZ7tpotQEGDQVlU+1t+ATH6clj7bqZisEEM8gK8JlEcSp72tiUewoQTs8tafqJeuMkJ9YEqD1mO+OBxZpfbyo7JM0RcK6QXbRiPeEyK5erjomCYN8Kz+Gs22gg2VJFvK0HOI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883377; c=relaxed/simple; bh=wyq3fGXBf1/wf4HczWQ2bhOAhHPvHxyeqpjD6GhMr0s=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=mVY5+V9MJeL5okhZHNo9uDWbop2pR2cIM/v7DkAqgz1wua9YC1tkmbKbTfDAfExWzLpPozFeUz6J2gEa5+RepIUJtSaaCfdNEmrHpBD0XV5RMIApUqcTBZp+wRSf6FsPoCjRxp9U2nS+DXiAlOabD+zQMQp9AjFggX5bvNTtbPw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OOLC5Hqj; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OOLC5Hqj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2857DC2BD10; Tue, 2 Jul 2024 01:22:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883377; bh=wyq3fGXBf1/wf4HczWQ2bhOAhHPvHxyeqpjD6GhMr0s=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=OOLC5HqjjPwDm+z33FIzF674zEbMA7axyXFZi4a2OhPAPbko+m7s6qHinmrsGScwb X3JF+bsHWpKHB+gQDINbqHLBFlw9CZEXuAUG2vXB7Sqhi9cAZrj/8UWDdXFqRxMQls TfD01M2J58EmDal99wvGvVb2Wmai30fX3m1ldOSgGe6RH8RpBwTVJ3mQy6K3dNRl/+ 0h7DqyeSoa11xJ6NHFAz1Oc6cFvYmYbWqDyspV+BxlywhM++j0wFwb5yYBw9Q1DhbE klTaaXpkkgjSinPr0eRGEglL6GTzCK2RwQnJcEZlX+I8CWym5E1o4MUWGxf2w6k1YJ nK5OrF7hxrvjg== Date: Mon, 01 Jul 2024 18:22:56 -0700 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, hch@lst.de Message-ID: <171988123196.2012546.11764956166230017274.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- scrub/scrub.c | 52 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/scrub/scrub.c b/scrub/scrub.c index 1b0609e7418b..c4b4367e458f 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 Tue Jul 2 01:23:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718878 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 E8D0238B for ; Tue, 2 Jul 2024 01:23:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883393; cv=none; b=pbclaPFRkVfFjtk0Bg7ho0UOhZU1Dco16SYYDvpDISysWMAFx8zCgipnTJt3Gm6T7eRRAUbLQlv3XrLKAgaJky0n0OlI0vTXXgamwMJF02dcHFRM9V//g9rcn50oVzzEu1XO61Bvjujt10lpkxgOKOGftAqsUmZy78Qbsow3uUg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883393; c=relaxed/simple; bh=iKqnMeyIew8hY6Psz62yCGimihIXwufc4I6mvnBvWRU=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=sFzkC6Re6csMwnZrynni94hnSb9kxMnpXHyHZoH4xkSXZTNM0JETskNkkId0LaNAchHATGZJ7rmZHoAOxQYxYK456rM6rOgK/3ePfPDz9ctmclr1q/MzUXKuLLtKOgJ949njue73GB7nQ/ZMVx7Opc4axKg9w3/+ljM1jA1BIiI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=vJHHJK+p; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="vJHHJK+p" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B7DA7C116B1; Tue, 2 Jul 2024 01:23:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883392; bh=iKqnMeyIew8hY6Psz62yCGimihIXwufc4I6mvnBvWRU=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=vJHHJK+pmxBRK5gKzJ6rSj9ithvLipiZYcSaGSeqAeSSFEFFeGl7QkIr+DBw3p6Dv R5FVbHGzUhRGCXJla6FCMwCUl/Bu5N22ENGSUK1jCqjvJOVeXZmLhLwIp18XSlpFyw 7rjMjcXaI/hH1FzFA96EBo5T4yMSlSEP8sgmDf9RCMf7Ffp7sw+89FzqtMVP58K6XG qhwLF6QXO0Jz3K7QMkn88cA88kK5uAoreriJjpyaU9RqPBWga4B1RapI8NqKejGTQE PBV7TVqwB0HL9fqCCPgjmapiE+Lxh4bKGrlIHLxtKJXak7hfU8cCeTFb5uwxkP16QU ScWDptltEs3gg== Date: Mon, 01 Jul 2024 18:23:12 -0700 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, hch@lst.de Message-ID: <171988123211.2012546.6968675746772396260.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- scrub/repair.c | 69 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/scrub/repair.c b/scrub/repair.c index 4fed86134eda..0b99e3351918 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 Tue Jul 2 01:23:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718879 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 D6FF76FCB for ; Tue, 2 Jul 2024 01:23:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883408; cv=none; b=r7g6v+0ALchsIyBdr26LnIXfFLQMAeT/FKAQvEfZ7FjiLHZYWZV0pkLt9a+6qtVQu0BXOyzm0cUoMy/fY2Rjkq5RFDwGAMHVe5gQUCNID+W4kqBmk4EpKPHhU135qBoYGfaPOpq+dxfz21HjPZnLqFg61PwaCy7iyBcnWnCex4w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883408; c=relaxed/simple; bh=5OqvX+At9aVRakQ6Gmkzzm6m1F6fLNst6tnwIMdABOM=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Wfghs8/W/B8tOpxmJBEQ+a6sVW0mQA7+G2Evu0Ah9v5BJtIhncnanGcA9pERWC20XPM3n+qjU80i+O7Q+IcfB8UkqXFPCEQSWyOGm+kZct4DPSV2W70qG8YUXqcRWRuySrtE+W/xw2r8Uh0Wjw0x1B47AxWKPEDKMOEQ7rbWkVE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=J7CCrOFY; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="J7CCrOFY" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 64A7BC2BD10; Tue, 2 Jul 2024 01:23:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883408; bh=5OqvX+At9aVRakQ6Gmkzzm6m1F6fLNst6tnwIMdABOM=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=J7CCrOFYJkvDzCSiF/fLK3cUg9icZJlpIpBpkyO4v3uQUB60xp6yULLd5yZO3zV9g ElYIcQw53afVG1IhReZ4AYtKijEcgEDuNIdI1ovntQ6uwX4507TaLRkMO6HPpPiaZR gKoCe7YqqFHWociw8hYer0iIS2PpbGMJ4la6b44TYmLIOwmrDVodeDL4RmWCC19uFk ptjSGEMaQvseUK3C3bQ/2SwI2agGsVB0/b7EfFB7QF2VDq584C6WFtiouxRC2bd+lG pw+5FElwF6rZBiLdEEyQ6SYWYur86N3+SkgQHxiEi9Pv3sk2kD5ASGQR+fjvjE44g5 sc6SaGcRHln6g== Date: Mon, 01 Jul 2024 18:23:27 -0700 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, hch@lst.de Message-ID: <171988123226.2012546.5857433986892075792.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- 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 0b99e3351918..7a710a159e69 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 c4b4367e458f..2fb2293558e5 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 bcfabda16be1..98a9238f2aac 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 Tue Jul 2 01:23:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718880 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 4A02B38B for ; Tue, 2 Jul 2024 01:23:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883424; cv=none; b=E+Sz1jYnQzZuUW1qk1K9ciY8zDUTa63+D8+9pAKRUrY+2fIXZJcM2DM/7orAidwcvI+15eMMHvVl9+fW9qm7hYxcpdgn/e6BpPseuJjD/0c+ygFTXJgqOaywIGKVnsx68cZe8AvbrCkm8hkZWSONeyclqyKrA+X1cS0orIAwDaA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883424; c=relaxed/simple; bh=IWNZ1PkRN53Uxg70RrgLSIVbDsck7WbFyz4QknxKkB8=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=oiDLN4EvCI0FX7WXsJk4qfVZaF055r89kqwl2GxOBIpEg99nGBKP4+fYmopDtcUcdKd+H87lSECB6/z/jyjt1+FbK9zMhtnhk+1jxTNsh6Br2EnIxnEq+cSms+Q0cxQZE+YoNakuYR9r9ZyXgnF8v/UnvDLCG3MHORYS7W4ianE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ZeTx0dak; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ZeTx0dak" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 19E51C116B1; Tue, 2 Jul 2024 01:23:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883424; bh=IWNZ1PkRN53Uxg70RrgLSIVbDsck7WbFyz4QknxKkB8=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=ZeTx0dakmFhBR/s046PYJpY6tI47lCYohrZPL67KwWbJqjE1ZVi6WJXB7MDx+c3tq SnytDdSYXUq9Seip1UxK2h4PvU+pjMHskNWe/ix0J9e6QlG7O8ul2KR5aJFX/MZ9lw 0GP7R9ygsMJtgrGiWnBgmNYX+w/9sNKH4I2CectQIy/MEPCHMw4lKnFQvsn8u6OkrX 29TEUs3wjcDmY5WTqdA4roomqhmmOhfIjyDigGKJfMATn5ZrkaW3IHgAUMhZPYibEQ aNYmkU6RTUnLL43AiNSK5f0Gs8mYlodGU2HF8JVstxqWiU0ibqoMhIUJhBr5oUv2Wd mUesPEcGngy3A== Date: Mon, 01 Jul 2024 18:23:43 -0700 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, hch@lst.de Message-ID: <171988123242.2012546.539108529178024852.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- scrub/phase1.c | 2 scrub/scrub.c | 277 ++++++++++++++++++++++++++++++++++++------------- scrub/scrub.h | 2 scrub/scrub_private.h | 16 +++ scrub/xfs_scrub.c | 1 5 files changed, 225 insertions(+), 73 deletions(-) diff --git a/scrub/phase1.c b/scrub/phase1.c index 095c045915a7..091b59e57e7b 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 2fb2293558e5..0c77f947244a 100644 --- a/scrub/scrub.c +++ b/scrub/scrub.c @@ -22,11 +22,48 @@ #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. */ +int +format_scrubv_descr( + struct scrub_ctx *ctx, + char *buf, + size_t buflen, + void *where) +{ + struct scrubv_descr *vdesc = where; + struct xfrog_scrubv *scrubv = vdesc->scrubv; + struct xfs_scrub_vec_head *vhead = &scrubv->head; + const struct xfrog_scrub_descr *sc; + unsigned int scrub_type; + + if (vdesc->idx >= 0) + scrub_type = scrubv->vectors[vdesc->idx].sv_type; + else if (scrubv->head.svh_nr > 0) + scrub_type = scrubv->vectors[scrubv->head.svh_nr - 1].sv_type; + else + scrub_type = XFS_SCRUB_TYPE_PROBE; + sc = &xfrog_scrubbers[scrub_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 +117,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 +248,87 @@ _("Optimization is possible.")); return 0; } +/* Fill out the scrub vector header from a scrub item. */ +void +xfrog_scrubv_from_item( + struct xfrog_scrubv *scrubv, + const struct scrub_item *sri) +{ + xfrog_scrubv_init(scrubv); + + if (bg_mode > 1) + scrubv->head.svh_rest_us = bg_mode - 1; + if (sri->sri_agno != -1) + scrubv->head.svh_agno = sri->sri_agno; + if (sri->sri_ino != -1ULL) { + scrubv->head.svh_ino = sri->sri_ino; + scrubv->head.svh_gen = sri->sri_gen; + } +} + +/* Add a scrubber to the scrub vector. */ +void +xfrog_scrubv_add_item( + struct xfrog_scrubv *scrubv, + const struct scrub_item *sri, + unsigned int scrub_type) +{ + struct xfs_scrub_vec *v; + + v = xfrog_scrubv_next_vector(scrubv); + v->sv_type = scrub_type; +} + +/* 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 xfrog_scrubv scrubv = { }; + struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv); + struct xfs_scrub_vec *v; + unsigned int scrub_type; + int error; + + assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); + + xfrog_scrubv_from_item(&scrubv, sri); + descr_set(&dsc, &vdesc); + + foreach_scrub_type(scrub_type) { + if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK)) + continue; + xfrog_scrubv_add_item(&scrubv, 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, &scrubv); + if (error) + return error; + + foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, 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 +364,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 +423,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 +456,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 +472,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 +675,21 @@ 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 xfrog_scrubv scrubv = { }; + + xfrog_scrubv_init(&scrubv); + + if (debug_tweak_on("XFS_SCRUB_FORCE_SINGLE")) + ctx->mnt.flags |= XFROG_FLAG_SCRUB_FORCE_SINGLE; + + /* + * We set the fallback flag if calling the kernel with a zero-length + * vector doesn't work. + */ + xfrog_scrubv_metadata(&ctx->mnt, &scrubv); +} diff --git a/scrub/scrub.h b/scrub/scrub.h index 90578108a1c8..183b89379cb4 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 98a9238f2aac..bf53ee5af2cf 100644 --- a/scrub/scrub_private.h +++ b/scrub/scrub_private.h @@ -8,9 +8,24 @@ /* Shared code between scrub.c and repair.c. */ +void xfrog_scrubv_from_item(struct xfrog_scrubv *scrubv, + const struct scrub_item *sri); +void xfrog_scrubv_add_item(struct xfrog_scrubv *scrubv, + const struct scrub_item *sri, unsigned int scrub_type); + int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen, void *where); +struct scrubv_descr { + struct xfrog_scrubv *scrubv; + int idx; +}; + +#define SCRUBV_DESCR(sv) { .scrubv = (sv), .idx = -1 } + +int format_scrubv_descr(struct scrub_ctx *ctx, char *buf, size_t buflen, + void *where); + /* Predicates for scrub flag state. */ static inline bool is_corrupt(const struct xfs_scrub_vec *sv) @@ -104,5 +119,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_ */ diff --git a/scrub/xfs_scrub.c b/scrub/xfs_scrub.c index bb316f73e02c..f5b58de12812 100644 --- a/scrub/xfs_scrub.c +++ b/scrub/xfs_scrub.c @@ -115,6 +115,7 @@ * XFS_SCRUB_THREADS -- start exactly this number of threads * XFS_SCRUB_DISK_ERROR_INTERVAL-- simulate a disk error every this many bytes * XFS_SCRUB_DISK_VERIFY_SKIP -- pretend disk verify read calls succeeded + * XFS_SCRUB_FORCE_SINGLE -- fall back to ioctl-per-item scrubbing * * Available even in non-debug mode: * SERVICE_MODE -- compress all error codes to 1 for LSB From patchwork Tue Jul 2 01:23:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718881 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 0C3E84A06 for ; Tue, 2 Jul 2024 01:24:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883440; cv=none; b=S3XcWPaBTxJGYHzPq9mfZPWFSHbFDKORwBKaHYimqNuFojoxZgx5sVXbMA4WvIIAhYmc/yG/NSVHOD1BU1tgWSXVmaQtpP6a3uEk/YSeB+cpypSEqGGvI+cP4fskfbGYfzAFbDbS6DVomaB10hSXy9fH6vMsUIHVG+84OSkMRMs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883440; c=relaxed/simple; bh=arcBTRK+D+nffItIq3REb2sVedMxpr+4mqp/9Yu2qwE=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=LMsOvxbfBhn/RpviMo6yhTzLU/1TzPsJTZY9H2hbtDzx15fh+2MHZnQwphd6amquUiwHkLgZfY2O7PNKUPyvKRjnVh7SPTgaB5vKA3FtlndZ5u+50QmUKWru1b8ugZ5zFbMiIK0IwoCpYqOB6VrM4HpQwieuqiNmiFXElREpPS0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=esNZ4c1l; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="esNZ4c1l" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C8D2CC116B1; Tue, 2 Jul 2024 01:23:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883439; bh=arcBTRK+D+nffItIq3REb2sVedMxpr+4mqp/9Yu2qwE=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=esNZ4c1lQAkjx6HydqloGHSvZh0aVx/GG2H3F9pySyk5Redyu4ydfFo0ndELYbILq 8JZBzLgwW2FeJU6pgY2jx/eVsZuK0GaLLvtw6uCASMKGMwMKcyQ4P/0klpiTcwoNbp iVzhgAKxCgHdj++e1Wdh3Cbdcv4GFwNHcCgbsSJvLuczG5+tcKi1bpfai/FCEBP3dR 37ApbvPHKqm5dXLETpAacy/hGjrLef9brJltjWYW39+UWE4gBQBdjtoI6Dn6n+1tj3 ArFT48kbONVVFMTMg2EM1Q5RTh3779lUPeBq8D1AHG98X/JSw8qn938zGRWneBVQzM B5OcaqoYdDkWQ== Date: Mon, 01 Jul 2024 18:23:59 -0700 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, hch@lst.de Message-ID: <171988123257.2012546.6149607312591068968.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- scrub/repair.c | 268 +++++++++++++++++++++++++++---------------------- scrub/scrub.c | 77 +++----------- scrub/scrub_private.h | 9 +- 3 files changed, 166 insertions(+), 188 deletions(-) diff --git a/scrub/repair.c b/scrub/repair.c index 7a710a159e69..6a5fd40fd02f 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,133 @@ _("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 xfrog_scrubv scrubv = { }; + struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv); + struct xfs_scrub_vec *v; + unsigned int scrub_type; + int error; + + assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); + + xfrog_scrubv_from_item(&scrubv, sri); + descr_set(&dsc, &vdesc); + + foreach_scrub_type(scrub_type) { + if (scrub_excessive_errors(ctx)) + return ECANCELED; + + if (!can_repair_now(sri, scrub_type, repair_mask, + repair_flags)) + continue; + + xfrog_scrubv_add_item(&scrubv, 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, &scrubv); + if (error) + return error; + + foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, 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 +707,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 +722,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 +742,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 0c77f947244a..d582dafbbe4e 100644 --- a/scrub/scrub.c +++ b/scrub/scrub.c @@ -64,35 +64,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( @@ -271,12 +242,17 @@ void xfrog_scrubv_add_item( struct xfrog_scrubv *scrubv, const struct scrub_item *sri, - unsigned int scrub_type) + unsigned int scrub_type, + bool want_repair) { struct xfs_scrub_vec *v; v = xfrog_scrubv_next_vector(scrubv); v->sv_type = scrub_type; + if (want_repair) + v->sv_flags |= XFS_SCRUB_IFLAG_REPAIR; + if (want_repair && use_force_rebuild) + v->sv_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD; } /* Do a read-only check of some metadata. */ @@ -301,7 +277,7 @@ scrub_call_kernel( foreach_scrub_type(scrub_type) { if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK)) continue; - xfrog_scrubv_add_item(&scrubv, sri, scrub_type); + xfrog_scrubv_add_item(&scrubv, sri, scrub_type, false); dbg_printf("check %s flags %xh tries %u\n", descr_render(&dsc), sri->sri_state[scrub_type], @@ -365,8 +341,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) @@ -382,6 +358,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]; @@ -395,34 +376,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. @@ -477,7 +430,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 bf53ee5af2cf..d9de18ce1795 100644 --- a/scrub/scrub_private.h +++ b/scrub/scrub_private.h @@ -11,10 +11,8 @@ void xfrog_scrubv_from_item(struct xfrog_scrubv *scrubv, const struct scrub_item *sri); void xfrog_scrubv_add_item(struct xfrog_scrubv *scrubv, - const struct scrub_item *sri, unsigned int scrub_type); - -int format_scrub_descr(struct scrub_ctx *ctx, char *buf, size_t buflen, - void *where); + const struct scrub_item *sri, unsigned int scrub_type, + bool want_repair); struct scrubv_descr { struct xfrog_scrubv *scrubv; @@ -116,8 +114,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 Tue Jul 2 01:24:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718882 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 AB4AC523D for ; Tue, 2 Jul 2024 01:24:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883455; cv=none; b=aT5FcWzndf9NOYZtA48ZI0HuR9+NVwisNrTmjMtXo5XTjNP9h3sClniZ5pofmMTQGMG4942Y3eTC/LZw4/wmID+sGbzGkl6s/FjUKPvpoIUQki2WUuxEvvv50FH59mXnh7WrPzvzwvWG2pbs1IxO2vXS+ChXTaTh2yC6ItCPIDU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883455; c=relaxed/simple; bh=vXQda1ym4ryuyWdLnZZsSqG0NvdKnBgO32U/0yhnIw8=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=KVlBeXkIYBoNLrHEHhIeiCZnHtKyc5/QDb4eUmDVpzFwO+UxtB0zJs8L6ah83dUZN92rlI0aroo55+qFUWUEu3eOrx/V/Yc2MIAxZaNbwBr7+0SJ1q5RlXGD8VPTov2Y+LtqD7dfowkUeRnPUkP2NwXLhppatXn/IIi8qqnJ0eQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=N/eLqkXF; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="N/eLqkXF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7AB02C116B1; Tue, 2 Jul 2024 01:24:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883455; bh=vXQda1ym4ryuyWdLnZZsSqG0NvdKnBgO32U/0yhnIw8=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=N/eLqkXF+5+wC8d92mngQIAQBerxIYglQpKctjJSxpdvOMgFSe3PXYmRfRDr1wiBD v/suS2Dq4PfMZt/fFBDHrCdoGbBYu+yOSKNdu5gNCfgQeQa/1ENM+SDVZr3JpWtQWy WhPxUJ7BNhAdhwW8zvg1c60whV6ynhImUnCYPaKpXtfXWTS93MNkJ1ugdfmeIjoHsy ioJ3mlZSwwJ08g72Xv7jQPBcuVNCn/2GwWaVJRUBHV+jmUG41qFOh0LP7ad3ctlTwj WmOAD4cUSC9qHepPkMNxlCIsH3uZ56TfohZLEomRClv9kFeZLJ1ojv/V3sZdElPO+Z 1fCdqjVBqBxyg== Date: Mon, 01 Jul 2024 18:24:15 -0700 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, hch@lst.de Message-ID: <171988123273.2012546.4398258276146368055.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- scrub/phase2.c | 15 ++-------- scrub/phase3.c | 17 +---------- scrub/repair.c | 32 +++++++++++++++++++- scrub/scrub.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++- scrub/scrub.h | 17 +++++++++++ scrub/scrub_private.h | 4 ++- 6 files changed, 130 insertions(+), 32 deletions(-) diff --git a/scrub/phase2.c b/scrub/phase2.c index 57c6d0ef2137..d435da07125a 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 98e5c5a1f9f4..09a1ea452bb9 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 6a5fd40fd02f..8a28f6b13d87 100644 --- a/scrub/repair.c +++ b/scrub/repair.c @@ -324,6 +324,7 @@ repair_call_kernel( struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv); struct xfs_scrub_vec *v; unsigned int scrub_type; + bool need_barrier = false; int error; assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); @@ -339,6 +340,11 @@ repair_call_kernel( repair_flags)) continue; + if (need_barrier) { + xfrog_scrubv_add_barrier(&scrubv); + need_barrier = false; + } + xfrog_scrubv_add_item(&scrubv, sri, scrub_type, true); if (sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSREPAIR) @@ -351,6 +357,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, &scrubv); @@ -358,6 +375,16 @@ repair_call_kernel( return error; foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, 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; @@ -446,7 +473,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. @@ -728,7 +756,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 d582dafbbe4e..44c4049899d2 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( @@ -255,6 +284,20 @@ xfrog_scrubv_add_item( v->sv_flags |= XFS_SCRUB_IFLAG_FORCE_REBUILD; } +/* Add a barrier to the scrub vector. */ +void +xfrog_scrubv_add_barrier( + struct xfrog_scrubv *scrubv) +{ + struct xfs_scrub_vec *v; + + v = xfrog_scrubv_next_vector(scrubv); + + 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; +} + /* Do a read-only check of some metadata. */ static int scrub_call_kernel( @@ -267,6 +310,7 @@ scrub_call_kernel( struct scrubv_descr vdesc = SCRUBV_DESCR(&scrubv); struct xfs_scrub_vec *v; unsigned int scrub_type; + bool need_barrier = false; int error; assert(!debug_tweak_on("XFS_SCRUB_NO_KERNEL")); @@ -277,8 +321,17 @@ scrub_call_kernel( foreach_scrub_type(scrub_type) { if (!(sri->sri_state[scrub_type] & SCRUB_ITEM_NEEDSCHECK)) continue; + + if (need_barrier) { + xfrog_scrubv_add_barrier(&scrubv); + need_barrier = false; + } + xfrog_scrubv_add_item(&scrubv, 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]); @@ -289,6 +342,16 @@ scrub_call_kernel( return error; foreach_xfrog_scrubv_vec(&scrubv, vdesc.idx, 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; @@ -383,15 +446,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++; } @@ -411,7 +484,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; /* diff --git a/scrub/scrub.h b/scrub/scrub.h index 183b89379cb4..c3eed1b261d5 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 d9de18ce1795..c5e0a7c08be0 100644 --- a/scrub/scrub_private.h +++ b/scrub/scrub_private.h @@ -13,6 +13,7 @@ void xfrog_scrubv_from_item(struct xfrog_scrubv *scrubv, void xfrog_scrubv_add_item(struct xfrog_scrubv *scrubv, const struct scrub_item *sri, unsigned int scrub_type, bool want_repair); +void xfrog_scrubv_add_barrier(struct xfrog_scrubv *scrubv); struct scrubv_descr { struct xfrog_scrubv *scrubv; @@ -116,6 +117,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 Tue Jul 2 01:24:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Darrick J. Wong" X-Patchwork-Id: 13718883 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 4C2738489 for ; Tue, 2 Jul 2024 01:24:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883471; cv=none; b=D1nb/x/yZCWB6jKjasAUziO7DnTTT/EK1lLr/IwJTMVPDT8+23JLznZGLoHI75s2HwYrNK/BQ6nDkg2NoHziTkATnPSHEWbvNcoJMvi8V4YCIox97kWT8Qty+haFKwic0mz3q2h4WwWsoLN4aKOdXfkgKQYDdbVkwljPLA/I5fA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719883471; c=relaxed/simple; bh=b+P2yXPiuwUO+6dZnsowJDwcTTb/DgS6bamypcUy8eo=; h=Date:Subject:From:To:Cc:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=RXnB2qeVXePstjO0OkSXmX7nTfDfUwq9oq6WBgNh46TAFwFDzcb6lRIYYrZNdhkbogIBtwqY7F8RzgkQb20DZ/bZ4wECqBifbfRv1HZIL17Sfw8hwlswL7bYDfa7rABKMqFpNmI08VCOyFVJ1bVBn0uoHHfoamofl0YLOdnEdho= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PFG/BwrA; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PFG/BwrA" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2472AC116B1; Tue, 2 Jul 2024 01:24:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1719883471; bh=b+P2yXPiuwUO+6dZnsowJDwcTTb/DgS6bamypcUy8eo=; h=Date:Subject:From:To:Cc:In-Reply-To:References:From; b=PFG/BwrAG0TFUbpyl3T0oaVBkWrf/FsxWyshoBycCYxcgsiPsJT2GpT4eB8Ar/Grp hrKDgtU4bZtouhUfgjZDwsqP8xSdIAzsKktDCO/Mh8JwOFYpiCydo5QF84H2Vye8tg jHrWbKT7vxaE5et4w86jwp4C3BIRYRZDPs7XXfxw6b8x/jJl7X4PyPtOh21ZDRdQvt tGumpnBJwZ/TZiHWYsdfibKl9agLSRzpjtfQ0OLiAezIl7VRJLtLrKI25T/PvdEgGi P/avZoDC6geocDcuWAjVCk6KnvD7IwdVgGjv6MEQHvXJcxYvip7Z6AyCCoVf80XkRW Eahkbu9BBK4ig== Date: Mon, 01 Jul 2024 18:24:30 -0700 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, hch@lst.de Message-ID: <171988123288.2012546.14926301961874768937.stgit@frogsfrogsfrogs> In-Reply-To: <171988123120.2012546.17403096510880884928.stgit@frogsfrogsfrogs> References: <171988123120.2012546.17403096510880884928.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 Reviewed-by: Christoph Hellwig --- 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 d435da07125a..c24d137358c7 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 09a1ea452bb9..046a42c1da8b 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 8a28f6b13d87..e594e704f515 100644 --- a/scrub/repair.c +++ b/scrub/repair.c @@ -860,6 +860,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; @@ -875,6 +876,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; }