Repository navigation
Conversation
1986857 to
e3b6b34
Compare
d180e83 to
d311aa1
Compare
|
The remaining CI failures are unrelated. |
alek-p
left a comment
There was a problem hiding this comment.
Thanks for bring this up this and submitting the PR. Your patch treats that leftover DTL as a scrub problem but it's really a symptom of resilver not completing in my oppinion. If I understand the problem correctly I think there is a tighter fix for this (not tested):
diff --git module/zfs/spa_checkpoint.c module/zfs/spa_checkpoint.c
index 870902237..84a4643ee 100644
--- module/zfs/spa_checkpoint.c
+++ module/zfs/spa_checkpoint.c
@@ -186,6 +186,13 @@ spa_checkpoint_discard_complete_sync(void *arg, dmu_tx_t *tx)
spa_history_log_internal(spa, "spa discard checkpoint", tx,
"finished discarding checkpointed state from the pool");
+
+ /*
+ * Resilver cannot excise DTL_MISSING while a checkpoint is active.
+ * Now that it is gone, finish any replacement that stalled.
+ */
+ if (vdev_resilver_needed(spa->spa_root_vdev, NULL, NULL))
+ spa_async_request(spa, SPA_ASYNC_RESILVER);
}
typedef struct spa_checkpoint_discard_sync_callback_arg {
The issue is that the checkpoint discard drops the reason for holding the DTL and then does nothing. The first resilver already walked the live tree and set DSS_FINISHED. Nothing starts a second pass, and problems follow from that.
When a scrub finds replacement DTLs, vdev_resilver_needed() narrows the scan to ranges requiring repair, but scrub I/O still reads every child of a replacing vdev. For a split indirect block, its BP-less segment read can compare valid data from the old child with incomplete data from the replacement, discard the valid result, and finish without repair. The scan can then excise the replacement DTL and detach the old child. Record the effective scan as a healing resilver so its I/O skips the DTL-missing child, repairs it, and records failed repair writes. Preserve the original scrub request in the persisted scan flags so status, cancel, pause, wait, and thorough scrub semantics remain unchanged. Add regressions that preserve replacement DTLs with a checkpoint. Verify scrub controls across export/import, target selection, repair, and replacement completion. Exercise split indirect mappings with an incomplete replacement and verify data integrity after the original child has detached and the pool has been exported and imported. Signed-off-by: Kamil Monicz <kamil@monicz.dev>
d311aa1 to
4922bba
Compare
|
@alek-p Starting a resilver after checkpoint discard could address that test setup. There is no checkpoint-discard operation in that retained sequence. The bug is that scan setup limits the scrub to the DTL ranges needing repair, I've also added a regression with split indirect mappings and an incomplete On the parent revision, this test reports a scan error, clears the replacement |
|
Sounds like the rebuild cancel path has the same issue as the checkpoint discard path? So adding something like this would help? It's hard to keep all the corner cases in my head but fundamentally I think a scrub is not responsible for DTL so should not be involved if we can avoid it? |
|
Yes, the rebuild-cancel path needed the same healing reassessment as checkpoint discard. I followed that through, and your point about scrub ownership changed the direction of the fix: scrub completion should not authorize clearing missing-write DTLs. I’ve dropped the approach of treating a DTL-limited scrub as a resilver while preserving its scrub identity. The replacement keeps verification and healing separate: a new scrub is rejected while available devices need healing, and missing-write ranges are retired only when the healing pass has sufficient coverage. It also includes reassessment after rebuild cancellation and checkpoint discard, as you suggested. The investigation produced three PRs:
|
Motivation and Context
A scrub started while a replacement still has outstanding dirty time log
(DTL) ranges can detach the original device before repairing the replacement.
dsl_scan_setup_sync()already callsvdev_resilver_needed()to limit thescan to those ranges, but previously retained
POOL_SCAN_SCRUBwhen theoriginal request was a scrub. That mismatch can lose the usable copy of a
split indirect block: its segment reads have no block pointer with which to
verify a checksum, so the replacing vdev compares the original's valid bytes
with the incomplete replacement and reports
ECKSUM. The indirect layerdiscards the segment. Scan completion can subsequently clear the DTL and
detach the original while the replacement is still incomplete.
This was found while investigating zloop run 32644998517 for #18826.
The retained pool history shows a canceled rebuild, followed by
scan setup func=1 mintxg=3 maxtxg=940, scan errors, and detachment of theoriginal child.
Description
Record the effective scan as
POOL_SCAN_RESILVER, allowing the existingresilver path to select the valid source, repair the replacement, and account
for failed repair writes.
Persist
DSF_SCRUB_REQUESTEDto preserve the original request for scrubstatus, pause/resume/cancel, wait, reopen, and thorough-scrub behavior.
The existing pool-history
funcfield continues to describe the requestedscan type. A DTL-limited scan does not advance
last_scrubbed_txgas thoughit had completed a full scrub.
How Has This Been Tested?
Both regressions use a checkpoint to retain replacement DTLs after an initial
resilver. They disable
resilver_deferto prevent an automatic retry fromconsuming those DTLs before the tested scrub starts.
replacement/scrub_resilver_classificationchecks scrub controls across apaused export/import, thorough-scrub resume, target selection, repair,
replacement completion, events, and
last_scrubbed_txg.replacement/scrub_resilver_splitconstructs split indirect mappings andan incomplete replacement. It verifies a mapped segment directly on each
leaf before repair, then checks the file after replacement completion and
export/import.
Both tests were exercised with debug kernel and userspace builds in an
isolated Debian 12 VM running Linux 6.1.0-53-cloud-amd64.
On the parent revision, the split-indirect regression established a good
original and an incomplete replacement. The scrub then reported an error,
cleared the replacement DTL, and detached the original. After export, import
failed with
insufficient replicas; the debug log reported an unreadableobsolete space map.
With the fix, the regression completes the replacement and verifies the
file contents and pool integrity with a post-import comparison and
zdb.Types of Changes
Checklist
Signed-off-by.