* [PATCH 01/11] xfs_scrub: handle missing media verify ioctl failure return codes
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
@ 2026-06-24 18:14 ` Darrick J. Wong
2026-06-25 12:00 ` Christoph Hellwig
2026-06-24 18:15 ` [PATCH 02/11] xfs_scrub: report bad file ranges correctly Darrick J. Wong
` (9 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:14 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Back when we reworked the read-verify code to use the kernel ioctl to
perform media scans, we forgot to teach read_verify_one callers to
handle the new error codes. Codex noticed this discrepancy, so let's
fix that.
Cc: <linux-xfs@vger.kernel.org> # v7.0.0
Fixes: 02760878dd86b9 ("xfs_scrub: use the verify media ioctl during phase 6 if possible")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/read_verify.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/scrub/read_verify.c b/scrub/read_verify.c
index 01e96f5bef40f6..efdf6d544858b8 100644
--- a/scrub/read_verify.c
+++ b/scrub/read_verify.c
@@ -383,7 +383,13 @@ read_verify(
read_error = errno;
/* Runtime error, bail out... */
- if (read_error != EIO && read_error != EILSEQ) {
+ switch (read_error) {
+ case EIO:
+ case EILSEQ:
+ case EREMOTEIO:
+ case ENODATA:
+ break;
+ default:
rvp->runtime_error = read_error;
return;
}
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 01/11] xfs_scrub: handle missing media verify ioctl failure return codes
2026-06-24 18:14 ` [PATCH 01/11] xfs_scrub: handle missing media verify ioctl failure return codes Darrick J. Wong
@ 2026-06-25 12:00 ` Christoph Hellwig
0 siblings, 0 replies; 23+ messages in thread
From: Christoph Hellwig @ 2026-06-25 12:00 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: aalbersh, linux-xfs, hch
On Wed, Jun 24, 2026 at 11:14:46AM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> Back when we reworked the read-verify code to use the kernel ioctl to
> perform media scans, we forgot to teach read_verify_one callers to
> handle the new error codes. Codex noticed this discrepancy, so let's
> fix that.
Urrg, more leaks of the obscure kernel BLK_STS to errno mappings to
userspace :( But yes, we need to handle them:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 02/11] xfs_scrub: report bad file ranges correctly
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
2026-06-24 18:14 ` [PATCH 01/11] xfs_scrub: handle missing media verify ioctl failure return codes Darrick J. Wong
@ 2026-06-24 18:15 ` Darrick J. Wong
2026-06-25 12:00 ` Christoph Hellwig
2026-06-24 18:15 ` [PATCH 03/11] xfs_scrub: handle media scans of internal rt devices correctly Darrick J. Wong
` (8 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:15 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Codex complains that the "media error at data offset..." message prints
the wrong information -- err_off is the offset into @map, not the file
offset; and the length should be constrained by the end of @map. Fix
both of these issues.
Cc: <linux-xfs@vger.kernel.org> # v4.15.0
Fixes: b364a9c008fc04 ("xfs_scrub: scrub file data blocks")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/phase6.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/scrub/phase6.c b/scrub/phase6.c
index cd9bb26bf88628..2278ae5ad3dfd7 100644
--- a/scrub/phase6.c
+++ b/scrub/phase6.c
@@ -408,6 +408,7 @@ report_ioerr_fsmap(
char buf[DESCR_BUFSZ];
struct ioerr_filerange *fr = arg;
uint64_t err_off;
+ uint64_t err_len;
int ret;
/* Don't care about unwritten extents. */
@@ -476,9 +477,12 @@ report_ioerr_fsmap(
return 0;
}
+ err_len = min(fr->physical + fr->length,
+ map->fmr_physical + map->fmr_length) -
+ max(fr->physical, map->fmr_physical);
str_unfixable_error(ctx, buf,
_("media error at data offset %llu length %llu."),
- err_off, fr->length);
+ map->fmr_offset + err_off, err_len);
return 0;
}
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 03/11] xfs_scrub: handle media scans of internal rt devices correctly
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
2026-06-24 18:14 ` [PATCH 01/11] xfs_scrub: handle missing media verify ioctl failure return codes Darrick J. Wong
2026-06-24 18:15 ` [PATCH 02/11] xfs_scrub: report bad file ranges correctly Darrick J. Wong
@ 2026-06-24 18:15 ` Darrick J. Wong
2026-06-25 12:01 ` Christoph Hellwig
2026-06-24 18:15 ` [PATCH 04/11] xfs_scrub: track inode scan abort state with an enum Darrick J. Wong
` (7 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:15 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Codex noticed media scans of internal rt devices don't work at all
correctly. First, we fail to allocate a ctx->verify_disks[XFS_DEV_RT]
for the internal rt section, and even if we did, phase 6 doesn't
allocate media_verify_state.rvp[XFS_DEV_RT] if there's a media error on
an internal rt volume, so we'll crash there too.
Fix both issues to make it work properly.
Cc: <linux-xfs@vger.kernel.org> # v6.15.0
Fixes: 37591ef3f4f14c ("xfs_scrub: support internal RT device")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/phase1.c | 7 +++++--
scrub/phase6.c | 4 ++--
scrub/spacemap.c | 2 +-
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 34a2b3aec030eb..6ac59264b50bb7 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -101,7 +101,8 @@ scrub_cleanup(
disk_close(ctx->verify_disks[XFS_DEV_DATA]);
if (ctx->verify_disks[XFS_DEV_LOG])
disk_close(ctx->verify_disks[XFS_DEV_LOG]);
- if (ctx->verify_disks[XFS_DEV_RT])
+ if (ctx->verify_disks[XFS_DEV_RT] &&
+ ctx->verify_disks[XFS_DEV_RT] != ctx->verify_disks[XFS_DEV_DATA])
disk_close(ctx->verify_disks[XFS_DEV_RT]);
fshandle_destroy();
error = -xfd_close(&ctx->mnt);
@@ -232,7 +233,9 @@ configure_xfs_verify_fallback(
}
}
- if (ctx->fsinfo.fs_rt) {
+ if (ctx->mnt.fsgeom.rtstart) {
+ ctx->verify_disks[XFS_DEV_RT] = ctx->verify_disks[XFS_DEV_DATA];
+ } else if (ctx->fsinfo.fs_rt || ctx->mnt.fsgeom.rtstart) {
ctx->verify_disks[XFS_DEV_RT] = disk_open(ctx->fsinfo.fs_rt);
if (!ctx->verify_disks[XFS_DEV_RT]) {
str_error(ctx, ctx->mntpoint,
diff --git a/scrub/phase6.c b/scrub/phase6.c
index 2278ae5ad3dfd7..aef817add4157b 100644
--- a/scrub/phase6.c
+++ b/scrub/phase6.c
@@ -744,7 +744,7 @@ phase6_func(
goto out_datapool;
}
}
- if (ctx->fsinfo.fs_rt) {
+ if (ctx->fsinfo.fs_rt || ctx->mnt.fsgeom.rtstart) {
ret = alloc_pool(ctx, &vs, XFS_DEV_RT);
if (ret) {
str_liberror(ctx, ret,
@@ -843,7 +843,7 @@ phase6_estimate(
* nr_threads appropriately to handle that many threads.
*/
*nr_threads = read_verify_nproc(ctx);
- if (ctx->fsinfo.fs_rt)
+ if (ctx->fsinfo.fs_rt || ctx->mnt.fsgeom.rtstart)
*nr_threads += read_verify_nproc(ctx);
if (ctx->fsinfo.fs_log)
*nr_threads += read_verify_nproc(ctx);
diff --git a/scrub/spacemap.c b/scrub/spacemap.c
index 1ee4d1946d3db7..8f595ad94c5991 100644
--- a/scrub/spacemap.c
+++ b/scrub/spacemap.c
@@ -266,7 +266,7 @@ scrub_scan_all_spacemaps(
break;
}
}
- if (ctx->fsinfo.fs_rt) {
+ if (ctx->fsinfo.fs_rt || ctx->mnt.fsgeom.rtstart) {
for (agno = 0; agno < ctx->mnt.fsgeom.rgcount; agno++) {
ret = -workqueue_add(&wq, scan_rtg_rmaps, agno, &sbx);
if (ret) {
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 03/11] xfs_scrub: handle media scans of internal rt devices correctly
2026-06-24 18:15 ` [PATCH 03/11] xfs_scrub: handle media scans of internal rt devices correctly Darrick J. Wong
@ 2026-06-25 12:01 ` Christoph Hellwig
0 siblings, 0 replies; 23+ messages in thread
From: Christoph Hellwig @ 2026-06-25 12:01 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: aalbersh, linux-xfs, hch
On Wed, Jun 24, 2026 at 11:15:17AM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> Codex noticed media scans of internal rt devices don't work at all
> correctly. First, we fail to allocate a ctx->verify_disks[XFS_DEV_RT]
> for the internal rt section, and even if we did, phase 6 doesn't
> allocate media_verify_state.rvp[XFS_DEV_RT] if there's a media error on
> an internal rt volume, so we'll crash there too.
>
> Fix both issues to make it work properly.
Do you have a test case for this?
The changes look good, but I'd love to have coverage for it...
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 04/11] xfs_scrub: track inode scan abort state with an enum
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
` (2 preceding siblings ...)
2026-06-24 18:15 ` [PATCH 03/11] xfs_scrub: handle media scans of internal rt devices correctly Darrick J. Wong
@ 2026-06-24 18:15 ` Darrick J. Wong
2026-06-25 12:02 ` Christoph Hellwig
2026-06-24 18:15 ` [PATCH 05/11] xfs_scrub: don't skip bulkstat batch when scrub_scan_user_files helper returns ESTALE Darrick J. Wong
` (6 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:15 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Change this from a boolean to an enum so that we can handle scan
cancellations correctly in the next patch.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/inodes.c | 44 +++++++++++++++++++++++++++-----------------
1 file changed, 27 insertions(+), 17 deletions(-)
diff --git a/scrub/inodes.c b/scrub/inodes.c
index ab5cf393327f1a..bf1cbdd6c7698b 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -357,13 +357,23 @@ bulkstat_for_inumbers(
bulkstat_single_step(ctx, inumbers, seen_mask, breq);
}
+enum abort_state {
+ RUNNING = 0,
+ ABORTED,
+};
+
+static inline int abort_state_ret(enum abort_state s)
+{
+ return s == ABORTED ? -1 : 0;
+}
+
/* BULKSTAT wrapper routines. */
struct scan_inodes {
struct workqueue wq_bulkstat;
scrub_inode_iter_fn fn;
void *arg;
unsigned int nr_threads;
- bool aborted;
+ enum abort_state aborted;
};
/*
@@ -530,7 +540,7 @@ scan_ag_bulkstat(
}
str_info(ctx, descr_render(&dsc_bulkstat),
_("Changed too many times during scan; giving up."));
- si->aborted = true;
+ si->aborted = ABORTED;
goto out;
}
case ECANCELED:
@@ -540,7 +550,7 @@ _("Changed too many times during scan; giving up."));
goto err;
}
if (scrub_excessive_errors(ctx)) {
- si->aborted = true;
+ si->aborted = ABORTED;
goto out;
}
last_ino = scan_ino;
@@ -549,7 +559,7 @@ _("Changed too many times during scan; giving up."));
err:
if (error) {
str_liberror(ctx, error, descr_render(&dsc_bulkstat));
- si->aborted = true;
+ si->aborted = ABORTED;
}
out:
free(ichunk);
@@ -594,7 +604,7 @@ scan_ag_inumbers(
cvt_ino_to_agino(&ctx->mnt, nextino),
cvt_ino_to_agino(&ctx->mnt,
ireq->inumbers[0].xi_startino));
- si->aborted = true;
+ si->aborted = ABORTED;
break;
}
nextino = ireq->hdr.ino;
@@ -611,7 +621,7 @@ scan_ag_inumbers(
error = -workqueue_add(&si->wq_bulkstat,
scan_ag_bulkstat, agno, ichunk);
if (error) {
- si->aborted = true;
+ si->aborted = ABORTED;
str_liberror(ctx, error,
_("queueing bulkstat work"));
goto out;
@@ -641,7 +651,7 @@ scan_ag_inumbers(
err:
if (error) {
str_liberror(ctx, error, descr_render(&dsc));
- si->aborted = true;
+ si->aborted = ABORTED;
}
out:
if (ichunk)
@@ -687,14 +697,14 @@ scrub_scan_all_inodes(
si.nr_threads);
if (ret) {
str_liberror(ctx, ret, _("creating inumbers workqueue"));
- si.aborted = true;
+ si.aborted = ABORTED;
goto kill_bulkstat;
}
for (agno = 0; agno < ctx->mnt.fsgeom.agcount; agno++) {
ret = -workqueue_add(&wq_inumbers, scan_ag_inumbers, agno, &si);
if (ret) {
- si.aborted = true;
+ si.aborted = ABORTED;
str_liberror(ctx, ret, _("queueing inumbers work"));
break;
}
@@ -702,7 +712,7 @@ scrub_scan_all_inodes(
ret = -workqueue_terminate(&wq_inumbers);
if (ret) {
- si.aborted = true;
+ si.aborted = ABORTED;
str_liberror(ctx, ret, _("finishing inumbers work"));
}
workqueue_destroy(&wq_inumbers);
@@ -710,12 +720,12 @@ scrub_scan_all_inodes(
kill_bulkstat:
ret = -workqueue_terminate(&si.wq_bulkstat);
if (ret) {
- si.aborted = true;
+ si.aborted = ABORTED;
str_liberror(ctx, ret, _("finishing bulkstat work"));
}
workqueue_destroy(&si.wq_bulkstat);
- return si.aborted ? -1 : 0;
+ return abort_state_ret(si.aborted);
}
struct user_bulkstat {
@@ -758,7 +768,7 @@ scan_user_files(
goto err;
}
if (scrub_excessive_errors(ctx)) {
- si->aborted = true;
+ si->aborted = ABORTED;
goto out;
}
}
@@ -766,7 +776,7 @@ scan_user_files(
err:
if (error) {
str_liberror(ctx, error, descr_render(&dsc_bulkstat));
- si->aborted = true;
+ si->aborted = ABORTED;
}
out:
free(ureq);
@@ -824,7 +834,7 @@ scan_user_bulkstat(
err_ureq:
free(ureq);
err:
- si->aborted = true;
+ si->aborted = ABORTED;
str_liberror(ctx, ret, what);
return 0;
}
@@ -861,12 +871,12 @@ scrub_scan_user_files(
ret = -workqueue_terminate(&si.wq_bulkstat);
if (ret) {
- si.aborted = true;
+ si.aborted = ABORTED;
str_liberror(ctx, ret, _("finishing bulkstat work"));
}
workqueue_destroy(&si.wq_bulkstat);
- return si.aborted ? -1 : 0;
+ return abort_state_ret(si.aborted);
}
/* Open a file by handle, returning either the fd or -1 on error. */
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 05/11] xfs_scrub: don't skip bulkstat batch when scrub_scan_user_files helper returns ESTALE
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
` (3 preceding siblings ...)
2026-06-24 18:15 ` [PATCH 04/11] xfs_scrub: track inode scan abort state with an enum Darrick J. Wong
@ 2026-06-24 18:15 ` Darrick J. Wong
2026-06-25 12:03 ` Christoph Hellwig
2026-06-24 18:16 ` [PATCH 06/11] xfs_scrub: warn about incomplete repairs if we never get to them Darrick J. Wong
` (5 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:15 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Codex complains that the ESTALE in the switch statement results in the
rest of the bulkstat batch being skipped, and that ECANCELED doesn't
actually abort the walk immediately. scrub_scan_user_files is only
called during phases 5 and 6, which is after we've verified all the file
metadata in the filesystem. Therefore, an ESTALE here means that the
file was deleted, so we skip it and move on to the next file. Fix both
issues.
Cc: <linux-xfs@vger.kernel.org> # v6.14.0
Fixes: 279b0d0e8d73f1 ("xfs_scrub: call bulkstat directly if we're only scanning user files")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/inodes.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/scrub/inodes.c b/scrub/inodes.c
index bf1cbdd6c7698b..7ce5f7ffeb62a1 100644
--- a/scrub/inodes.c
+++ b/scrub/inodes.c
@@ -360,6 +360,7 @@ bulkstat_for_inumbers(
enum abort_state {
RUNNING = 0,
ABORTED,
+ CANCELLED,
};
static inline int abort_state_ret(enum abort_state s)
@@ -544,8 +545,8 @@ _("Changed too many times during scan; giving up."));
goto out;
}
case ECANCELED:
- error = 0;
- fallthrough;
+ si->aborted = CANCELLED;
+ goto out;
default:
goto err;
}
@@ -761,9 +762,22 @@ scan_user_files(
case 0:
break;
case ESTALE:
- case ECANCELED:
+ /*
+ * scrub_scan_user_files is only called during phases
+ * 5 and 6, which is after we've verified all the file
+ * metadata in the filesystem. Therefore, an ESTALE
+ * here means that the file was deleted, so we skip it
+ * and move on to the next file.
+ */
error = 0;
- fallthrough;
+ break;
+ case ECANCELED:
+ /*
+ * Helper function wants us to stop iterating, so stop
+ * the walk immediately.
+ */
+ si->aborted = CANCELLED;
+ goto out;
default:
goto err;
}
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 05/11] xfs_scrub: don't skip bulkstat batch when scrub_scan_user_files helper returns ESTALE
2026-06-24 18:15 ` [PATCH 05/11] xfs_scrub: don't skip bulkstat batch when scrub_scan_user_files helper returns ESTALE Darrick J. Wong
@ 2026-06-25 12:03 ` Christoph Hellwig
0 siblings, 0 replies; 23+ messages in thread
From: Christoph Hellwig @ 2026-06-25 12:03 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: aalbersh, linux-xfs, hch
On Wed, Jun 24, 2026 at 11:15:48AM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> Codex complains that the ESTALE in the switch statement results in the
> rest of the bulkstat batch being skipped, and that ECANCELED doesn't
> actually abort the walk immediately. scrub_scan_user_files is only
> called during phases 5 and 6, which is after we've verified all the file
> metadata in the filesystem. Therefore, an ESTALE here means that the
> file was deleted, so we skip it and move on to the next file. Fix both
> issues.
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 06/11] xfs_scrub: warn about incomplete repairs if we never get to them
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
` (4 preceding siblings ...)
2026-06-24 18:15 ` [PATCH 05/11] xfs_scrub: don't skip bulkstat batch when scrub_scan_user_files helper returns ESTALE Darrick J. Wong
@ 2026-06-24 18:16 ` Darrick J. Wong
2026-06-25 12:03 ` Christoph Hellwig
2026-06-24 18:16 ` [PATCH 07/11] xfs_scrub: report external log space usage in phase 7 Darrick J. Wong
` (4 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:16 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If the final pass at repairs fails because the kernel says the
filesystem is busy, we should emit this error to the caller instead of
dropping it silently.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/repair.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/scrub/repair.c b/scrub/repair.c
index 5d821655877b80..fdefc2131aa453 100644
--- a/scrub/repair.c
+++ b/scrub/repair.c
@@ -110,7 +110,10 @@ repair_epilogue(
case EDEADLOCK:
case EBUSY:
/* Filesystem is busy, try again later. */
- if (debug || verbose)
+ if (repair_flags & XRM_FINAL_WARNING)
+ str_error(ctx, descr_render(dsc),
+_("Filesystem is busy, repair incomplete."));
+ else if (debug || verbose)
str_info(ctx, descr_render(dsc),
_("Filesystem is busy, deferring repair."));
return 0;
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 07/11] xfs_scrub: report external log space usage in phase 7
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
` (5 preceding siblings ...)
2026-06-24 18:16 ` [PATCH 06/11] xfs_scrub: warn about incomplete repairs if we never get to them Darrick J. Wong
@ 2026-06-24 18:16 ` Darrick J. Wong
2026-06-25 12:05 ` Christoph Hellwig
2026-06-24 18:16 ` [PATCH 08/11] xfs_scrub: account only data extent tail after an overlap Darrick J. Wong
` (3 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:16 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Let's report the external log space attached to a mounted filesystem so
that the user knows we found it.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/phase7.c | 73 +++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 48 insertions(+), 25 deletions(-)
diff --git a/scrub/phase7.c b/scrub/phase7.c
index e16ca28aa28371..3b765931a304a9 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -26,6 +26,7 @@
struct summary_counts {
unsigned long long dbytes; /* data dev bytes */
unsigned long long rbytes; /* rt dev bytes */
+ unsigned long long lbytes; /* log dev bytes */
unsigned long long next_phys; /* next phys bytes we see? */
unsigned long long agbytes; /* freespace bytes */
@@ -68,21 +69,18 @@ count_block_summary(
void *arg)
{
struct summary_counts *counts;
- bool is_rt = false;
+ enum xfs_device dev;
unsigned long long len;
int ret;
- if (ctx->mnt.fsgeom.rtstart) {
- if (fsmap->fmr_device == XFS_DEV_LOG)
- return 0;
- if (fsmap->fmr_device == XFS_DEV_RT)
- is_rt = true;
- } else {
- if (fsmap->fmr_device == ctx->fsinfo.fs_logdev)
- return 0;
- if (fsmap->fmr_device == ctx->fsinfo.fs_rtdev)
- is_rt = true;
- }
+ if (ctx->mnt.fsgeom.rtstart)
+ dev = fsmap->fmr_device;
+ else if (fsmap->fmr_device == ctx->fsinfo.fs_logdev)
+ dev = XFS_DEV_LOG;
+ else if (fsmap->fmr_device == ctx->fsinfo.fs_rtdev)
+ dev = XFS_DEV_RT;
+ else
+ dev = XFS_DEV_DATA;
counts = ptvar_get((struct ptvar *)arg, &ret);
if (ret) {
@@ -95,10 +93,16 @@ count_block_summary(
uint64_t blocks;
blocks = cvt_b_to_off_fsbt(&ctx->mnt, fsmap->fmr_length);
- if (is_rt)
+ switch (dev) {
+ case XFS_DEV_RT:
hist_add(&counts->rtdev_hist, blocks);
- else
+ break;
+ case XFS_DEV_DATA:
hist_add(&counts->datadev_hist, blocks);
+ break;
+ case XFS_DEV_LOG:
+ break;
+ }
return 0;
}
@@ -109,10 +113,16 @@ count_block_summary(
fsmap->fmr_owner == XFS_FMR_OWN_AG)
counts->agbytes += fsmap->fmr_length;
- if (is_rt) {
+ switch (dev) {
+ case XFS_DEV_LOG:
+ /* Count external log */
+ counts->lbytes += len;
+ break;
+ case XFS_DEV_RT:
/* Count realtime extents. */
counts->rbytes += len;
- } else {
+ break;
+ case XFS_DEV_DATA:
/* Count datadev extents. */
if (counts->next_phys >= fsmap->fmr_physical + len)
return 0;
@@ -120,6 +130,7 @@ count_block_summary(
len = counts->next_phys - fsmap->fmr_physical;
counts->dbytes += len;
counts->next_phys = fsmap->fmr_physical + fsmap->fmr_length;
+ break;
}
return 0;
@@ -137,6 +148,7 @@ add_summaries(
total->dbytes += item->dbytes;
total->rbytes += item->rbytes;
+ total->lbytes += item->lbytes;
total->agbytes += item->agbytes;
hist_import(&total->datadev_hist, &item->datadev_hist);
@@ -162,8 +174,10 @@ phase7_func(
unsigned long long used_data;
unsigned long long used_rt;
unsigned long long used_files;
+ unsigned long long used_log;
unsigned long long stat_data;
unsigned long long stat_rt;
+ unsigned long long stat_log;
uint64_t counted_inodes = 0;
unsigned long long absdiff;
unsigned long long d_blocks;
@@ -241,8 +255,13 @@ phase7_func(
/* Report on what we found. */
used_data = cvt_off_fsb_to_b(&ctx->mnt, d_blocks - d_bfree);
used_rt = cvt_off_fsb_to_b(&ctx->mnt, r_blocks - r_bfree);
+ if (ctx->mnt.fsgeom.logstart == 0)
+ used_log = cvt_off_fsb_to_b(&ctx->mnt, l_blocks);
+ else
+ used_log = 0;
stat_data = totalcount.dbytes;
stat_rt = totalcount.rbytes;
+ stat_log = totalcount.lbytes;
/*
* Complain if the counts are off by more than 10% unless
@@ -252,28 +271,32 @@ phase7_func(
complain = verbose;
complain |= !within_range(ctx, stat_data, used_data, absdiff, 1, 10,
_("data blocks"));
+ complain |= !within_range(ctx, stat_log, used_log, absdiff, 1, 10,
+ _("external log blocks"));
complain |= !within_range(ctx, stat_rt, used_rt, absdiff, 1, 10,
_("realtime blocks"));
complain |= !within_range(ctx, counted_inodes, used_files, 100, 1, 10,
_("inodes"));
if (complain) {
- double d, r, i;
- char *du, *ru, *iu;
+ double d, r, i, l;
+ char *du, *ru, *iu, *lu;
- if (used_rt || stat_rt) {
+ if (used_rt || stat_rt || used_log) {
d = auto_space_units(used_data, &du);
r = auto_space_units(used_rt, &ru);
+ l = auto_space_units(used_log, &lu);
i = auto_units(used_files, &iu, &ip);
fprintf(stdout,
-_("%.1f%s data used; %.1f%s realtime data used; %.*f%s inodes used.\n"),
- d, du, r, ru, ip, i, iu);
+_("%.1f%s data used; %.1f%s realtime data used; %.1f%s external log used; %.*f%s inodes used.\n"),
+ d, du, r, ru, l, lu, ip, i, iu);
d = auto_space_units(stat_data, &du);
r = auto_space_units(stat_rt, &ru);
+ l = auto_space_units(stat_log, &lu);
i = auto_units(counted_inodes, &iu, &ip);
fprintf(stdout,
-_("%.1f%s data found; %.1f%s realtime data found; %.*f%s inodes found.\n"),
- d, du, r, ru, ip, i, iu);
+_("%.1f%s data found; %.1f%s realtime data found; %.1f%s external log found; %.*f%s inodes found.\n"),
+ d, du, r, ru, l, lu, ip, i, iu);
} else {
d = auto_space_units(used_data, &du);
i = auto_units(used_files, &iu, &ip);
@@ -314,13 +337,13 @@ _("%.*f%s inodes counted; %.*f%s inodes checked.\n"),
*/
if (ctx->bytes_checked &&
(verbose ||
- !within_range(ctx, used_data + used_rt,
+ !within_range(ctx, used_data + used_rt + used_log,
ctx->bytes_checked, absdiff, 1, 10,
_("verified blocks")))) {
double b1, b2;
char *b1u, *b2u;
- b1 = auto_space_units(used_data + used_rt, &b1u);
+ b1 = auto_space_units(used_data + used_rt + used_log, &b1u);
b2 = auto_space_units(ctx->bytes_checked, &b2u);
fprintf(stdout,
_("%.1f%s data counted; %.1f%s data verified.\n"),
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 08/11] xfs_scrub: account only data extent tail after an overlap
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
` (6 preceding siblings ...)
2026-06-24 18:16 ` [PATCH 07/11] xfs_scrub: report external log space usage in phase 7 Darrick J. Wong
@ 2026-06-24 18:16 ` Darrick J. Wong
2026-06-25 12:05 ` Christoph Hellwig
2026-06-24 18:16 ` [PATCH 09/11] xfs_scrub: account for reflinked realtime file data Darrick J. Wong
` (2 subsequent siblings)
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:16 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Codex points out that the fsmap overlap handling in phase7 isn't quite
right -- if we already saw part of the current fsmapping, we should
*subtract* the overlap, not set the length to it! Fix that.
Cc: <linux-xfs@vger.kernel.org> # v4.15.0
Fixes: 698c6c7cb8ba75 ("xfs_scrub: check summary counters")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/phase7.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scrub/phase7.c b/scrub/phase7.c
index 3b765931a304a9..4a25c521fa0c76 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -127,7 +127,7 @@ count_block_summary(
if (counts->next_phys >= fsmap->fmr_physical + len)
return 0;
else if (counts->next_phys > fsmap->fmr_physical)
- len = counts->next_phys - fsmap->fmr_physical;
+ len -= counts->next_phys - fsmap->fmr_physical;
counts->dbytes += len;
counts->next_phys = fsmap->fmr_physical + fsmap->fmr_length;
break;
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 09/11] xfs_scrub: account for reflinked realtime file data
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
` (7 preceding siblings ...)
2026-06-24 18:16 ` [PATCH 08/11] xfs_scrub: account only data extent tail after an overlap Darrick J. Wong
@ 2026-06-24 18:16 ` Darrick J. Wong
2026-06-25 12:06 ` Christoph Hellwig
2026-06-24 18:17 ` [PATCH 10/11] xfs_scrub: don't leak the autofsck fsproperty handle Darrick J. Wong
2026-06-24 18:17 ` [PATCH 11/11] xfs_scrub: warn about difficult rtgroup repairs Darrick J. Wong
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:16 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
When we added reflink to the rt device, we forgot to account for
multiply owned space in the phase 7 accounting. Luckily, Codex noticed
for us.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/phase7.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/scrub/phase7.c b/scrub/phase7.c
index 4a25c521fa0c76..375ba0632c353b 100644
--- a/scrub/phase7.c
+++ b/scrub/phase7.c
@@ -27,7 +27,8 @@ struct summary_counts {
unsigned long long dbytes; /* data dev bytes */
unsigned long long rbytes; /* rt dev bytes */
unsigned long long lbytes; /* log dev bytes */
- unsigned long long next_phys; /* next phys bytes we see? */
+ unsigned long long next_dphys; /* next phys bytes we see on data dev? */
+ unsigned long long next_rphys; /* next phys bytes we see on rt dev? */
unsigned long long agbytes; /* freespace bytes */
/* Free space histogram, in fsb */
@@ -120,16 +121,21 @@ count_block_summary(
break;
case XFS_DEV_RT:
/* Count realtime extents. */
+ if (counts->next_rphys >= fsmap->fmr_physical + len)
+ return 0;
+ else if (counts->next_rphys > fsmap->fmr_physical)
+ len -= counts->next_rphys - fsmap->fmr_physical;
counts->rbytes += len;
+ counts->next_rphys = fsmap->fmr_physical + fsmap->fmr_length;
break;
case XFS_DEV_DATA:
/* Count datadev extents. */
- if (counts->next_phys >= fsmap->fmr_physical + len)
+ if (counts->next_dphys >= fsmap->fmr_physical + len)
return 0;
- else if (counts->next_phys > fsmap->fmr_physical)
- len -= counts->next_phys - fsmap->fmr_physical;
+ else if (counts->next_dphys > fsmap->fmr_physical)
+ len -= counts->next_dphys - fsmap->fmr_physical;
counts->dbytes += len;
- counts->next_phys = fsmap->fmr_physical + fsmap->fmr_length;
+ counts->next_dphys = fsmap->fmr_physical + fsmap->fmr_length;
break;
}
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 10/11] xfs_scrub: don't leak the autofsck fsproperty handle
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
` (8 preceding siblings ...)
2026-06-24 18:16 ` [PATCH 09/11] xfs_scrub: account for reflinked realtime file data Darrick J. Wong
@ 2026-06-24 18:17 ` Darrick J. Wong
2026-06-25 12:06 ` Christoph Hellwig
2026-06-24 18:17 ` [PATCH 11/11] xfs_scrub: warn about difficult rtgroup repairs Darrick J. Wong
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:17 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Codex notices that we leak the fsproperty handle if the filesystem
doesn't actually have the property set. Fix that by moving the free
call; it can handle a totally nulled out structure.
Cc: <linux-xfs@vger.kernel.org> # v6.10.0
Fixes: 9451b5ee0d0d2d ("xfs_scrub: allow sysadmin to control background scrubs")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/phase1.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/scrub/phase1.c b/scrub/phase1.c
index 6ac59264b50bb7..620a12393b3658 100644
--- a/scrub/phase1.c
+++ b/scrub/phase1.c
@@ -178,8 +178,6 @@ mode_from_autofsck(
break;
}
- fsprops_free_handle(&fph);
-
summarize:
switch (ctx->mode) {
case SCRUB_MODE_NONE:
@@ -200,6 +198,7 @@ mode_from_autofsck(
break;
}
+ fsprops_free_handle(&fph);
return;
no_property:
/*
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 11/11] xfs_scrub: warn about difficult rtgroup repairs
2026-06-24 18:14 [PATCHSET] xfs_scrub: codex-inspired bug fixes, part 2 Darrick J. Wong
` (9 preceding siblings ...)
2026-06-24 18:17 ` [PATCH 10/11] xfs_scrub: don't leak the autofsck fsproperty handle Darrick J. Wong
@ 2026-06-24 18:17 ` Darrick J. Wong
2026-06-25 12:06 ` Christoph Hellwig
10 siblings, 1 reply; 23+ messages in thread
From: Darrick J. Wong @ 2026-06-24 18:17 UTC (permalink / raw)
To: djwong, aalbersh; +Cc: linux-xfs, hch, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Codex noticed that rtgroup metadata repairs can be difficult (e.g.
rtrmap) but we don't ever warn the user about that. Scrub does that for
per-AG and fs metadata, so do that here too.
Cc: <linux-xfs@vger.kernel.org> # v6.13.0
Fixes: 241d915d69d4ae ("xfs_scrub: scrub realtime allocation group metadata")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
scrub/phase2.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/scrub/phase2.c b/scrub/phase2.c
index 8de77429f05d60..f382c1367be474 100644
--- a/scrub/phase2.c
+++ b/scrub/phase2.c
@@ -251,6 +251,7 @@ scan_rtgroup_metadata(
struct scrub_ctx *ctx = (struct scrub_ctx *)wq->wq_ctx;
struct scan_ctl *sctl = arg;
char descr[DESCR_BUFSZ];
+ unsigned int difficulty;
bool defer_repairs;
int ret;
@@ -281,7 +282,14 @@ scan_rtgroup_metadata(
sctl->aborted = true;
return;
}
+ if (defer_repairs)
+ goto defer;
+ /* Complain about metadata corruptions that might not be fixable. */
+ difficulty = repair_item_difficulty(&sri);
+ warn_repair_difficulties(ctx, difficulty, descr);
+
+defer:
/* Everything else gets fixed during phase 4. */
ret = defer_fs_repair(ctx, &sri);
if (ret) {
^ permalink raw reply related [flat|nested] 23+ messages in thread