Skip to content

Commit d828236

Browse files
author
Alek Pinchuk
committed
added module param to enable decompression of scrubed blocks
Signed-off-by: Alek Pinchuk <[email protected]>
1 parent 8302b6e commit d828236

File tree

6 files changed

+121
-3
lines changed

6 files changed

+121
-3
lines changed

man/man4/zfs.4

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
.\" Copyright (c) 2019, 2021 by Delphix. All rights reserved.
55
.\" Copyright (c) 2019 Datto Inc.
66
.\" Copyright (c) 2023, 2024, 2025, Klara, Inc.
7+
.\" Copyright (c) 2025 ConnectWise, Inc.
78
.\" The contents of this file are subject to the terms of the Common Development
89
.\" and Distribution License (the "License"). You may not use this file except
910
.\" in compliance with the License. You can obtain a copy of the license at
@@ -17,7 +18,7 @@
1718
.\" own identifying information:
1819
.\" Portions Copyright [yyyy] [name of copyright owner]
1920
.\"
20-
.Dd May 29, 2025
21+
.Dd June 30, 2025
2122
.Dt ZFS 4
2223
.Os
2324
.
@@ -2087,7 +2088,12 @@ working on a scrub between TXG flushes.
20872088
.It Sy zfs_scrub_error_blocks_per_txg Ns = Ns Sy 4096 Pq uint
20882089
Error blocks to be scrubbed in one txg.
20892090
.
2090-
.It Sy zfs_scan_checkpoint_intval Ns = Ns Sy 7200 Ns s Po 2 hour Pc Pq uint
2091+
.It Sy zfs_scrub_decompress Ns = Ns Sy 0 Ns | Ns 1 Pq uint
2092+
When set will cause scrub to decompress blocks it reads so that it will catch
2093+
the rare type of corruption where the checksum matches the data, but
2094+
decompression fails.
2095+
.
2096+
.It Sy zfs_scan_checkpoint_intval Ns = Ns Sy 7200 Ns s Po 2 hours Pc Pq uint
20912097
To preserve progress across reboots, the sequential scan algorithm periodically
20922098
needs to stop metadata scanning and issue all the verification I/O to disk.
20932099
The frequency of this flushing is determined by this tunable.

module/zfs/dsl_scan.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* Copyright (c) 2017, 2019, Datto Inc. All rights reserved.
2727
* Copyright (c) 2015, Nexenta Systems, Inc. All rights reserved.
2828
* Copyright 2019 Joyent, Inc.
29+
* Copyright 2025 ConnectWise, Inc.
2930
*/
3031

3132
#include <sys/dsl_scan.h>
@@ -56,6 +57,7 @@
5657
#include <sys/abd.h>
5758
#include <sys/range_tree.h>
5859
#include <sys/dbuf.h>
60+
#include <sys/fm/fs/zfs.h>
5961
#ifdef _KERNEL
6062
#include <sys/zfs_vfsops.h>
6163
#endif
@@ -246,6 +248,13 @@ static int zfs_free_bpobj_enabled = 1;
246248
/* Error blocks to be scrubbed in one txg. */
247249
static uint_t zfs_scrub_error_blocks_per_txg = 1 << 12;
248250

251+
/*
252+
* When set to a non-zero value will cause scrub to decompress blocks it
253+
* reads so that it will catch the rare type of corruption where the
254+
* checksum matches the data, but decompression fails.
255+
*/
256+
static uint_t zfs_scrub_decompress = 0;
257+
249258
/* the order has to match pool_scan_type */
250259
static scan_cb_t *scan_funcs[POOL_SCAN_FUNCS] = {
251260
NULL,
@@ -4874,6 +4883,41 @@ dsl_scan_scrub_done(zio_t *zio)
48744883
blkptr_t *bp = zio->io_bp;
48754884
dsl_scan_io_queue_t *queue = zio->io_private;
48764885

4886+
/*
4887+
* If the block was read without error, is compressed, and we're doing
4888+
* a decompression scrub we will now attempt to decompress it to
4889+
* further verify its integrity.
4890+
*/
4891+
if (zio->io_error == 0 && (BP_GET_COMPRESS(bp) != ZIO_COMPRESS_OFF) &&
4892+
zfs_scrub_decompress != 0) {
4893+
abd_t *dabd = abd_alloc_linear(BP_GET_LSIZE(bp), B_FALSE);
4894+
if (dabd != NULL) {
4895+
if (zio_decompress_data(BP_GET_COMPRESS(bp),
4896+
zio->io_abd, dabd,
4897+
abd_get_size(zio->io_abd), abd_get_size(dabd),
4898+
&zio->io_prop.zp_complevel) != 0) {
4899+
// checksum was valid but decompression failed
4900+
if (dsl_errorscrubbing(spa->spa_dsl_pool) &&
4901+
!dsl_errorscrub_is_paused(
4902+
spa->spa_dsl_pool->dp_scan)) {
4903+
atomic_inc_64(&spa->spa_dsl_pool->
4904+
dp_scan->errorscrub_phys
4905+
.dep_errors);
4906+
} else {
4907+
atomic_inc_64(&spa->spa_dsl_pool->
4908+
dp_scan->scn_phys.scn_errors);
4909+
}
4910+
// errlog this so it's in the zpool status -v
4911+
spa_log_error(zio->io_spa, &zio->io_bookmark,
4912+
BP_GET_LOGICAL_BIRTH(zio->io_bp));
4913+
(void) zfs_ereport_post(FM_EREPORT_ZFS_DATA,
4914+
zio->io_spa, NULL, &zio->io_bookmark,
4915+
zio, 0);
4916+
}
4917+
abd_free(dabd);
4918+
}
4919+
}
4920+
48774921
abd_free(zio->io_abd);
48784922

48794923
if (queue == NULL) {
@@ -5362,3 +5406,6 @@ ZFS_MODULE_PARAM(zfs, zfs_, resilver_defer_percent, UINT, ZMOD_RW,
53625406

53635407
ZFS_MODULE_PARAM(zfs, zfs_, scrub_error_blocks_per_txg, UINT, ZMOD_RW,
53645408
"Error blocks to be scrubbed in one txg");
5409+
5410+
ZFS_MODULE_PARAM(zfs, zfs_, scrub_decompress, UINT, ZMOD_RW,
5411+
"Scrub will decompress compressed blocks");

tests/runfiles/common.run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ tests = ['zpool_scrub_001_neg', 'zpool_scrub_002_pos', 'zpool_scrub_003_pos',
546546
'zpool_scrub_multiple_pools',
547547
'zpool_error_scrub_001_pos', 'zpool_error_scrub_002_pos',
548548
'zpool_error_scrub_003_pos', 'zpool_error_scrub_004_pos',
549-
'zpool_scrub_date_range_001']
549+
'zpool_scrub_date_range_001', 'zpool_scrub_decompress']
550550
tags = ['functional', 'cli_root', 'zpool_scrub']
551551

552552
[tests/functional/cli_root/zpool_set]

tests/zfs-tests/include/tunables.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ SCAN_LEGACY scan_legacy zfs_scan_legacy
8181
SCAN_SUSPEND_PROGRESS scan_suspend_progress zfs_scan_suspend_progress
8282
SCAN_VDEV_LIMIT scan_vdev_limit zfs_scan_vdev_limit
8383
SCRUB_AFTER_EXPAND scrub_after_expand zfs_scrub_after_expand
84+
SCRUB_DECOMPRESS scrub_decompress zfs_scrub_decompress
8485
SEND_HOLES_WITHOUT_BIRTH_TIME send_holes_without_birth_time send_holes_without_birth_time
8586
SLOW_IO_EVENTS_PER_SECOND slow_io_events_per_second zfs_slow_io_events_per_second
8687
SPA_ASIZE_INFLATION spa.asize_inflation spa_asize_inflation

tests/zfs-tests/tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
12501250
functional/cli_root/zpool_scrub/zpool_error_scrub_002_pos.ksh \
12511251
functional/cli_root/zpool_scrub/zpool_error_scrub_003_pos.ksh \
12521252
functional/cli_root/zpool_scrub/zpool_error_scrub_004_pos.ksh \
1253+
functional/cli_root/zpool_scrub/zpool_scrub_decompress.ksh \
12531254
functional/cli_root/zpool_set/cleanup.ksh \
12541255
functional/cli_root/zpool_set/setup.ksh \
12551256
functional/cli_root/zpool/setup.ksh \
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/bin/ksh -p
2+
# SPDX-License-Identifier: CDDL-1.0
3+
#
4+
# This file and its contents are supplied under the terms of the
5+
# Common Development and Distribution License ("CDDL"), version 1.0.
6+
# You may only use this file in accordance with the terms of version
7+
# 1.0 of the CDDL.
8+
#
9+
# A full copy of the text of the CDDL should have accompanied this
10+
# source. A copy of the CDDL is also available via the Internet at
11+
# http://www.illumos.org/license/CDDL.
12+
#
13+
14+
#
15+
# Copyright (c) 2025 ConnectWise Inc. All rights reserved.
16+
#
17+
18+
. $STF_SUITE/include/libtest.shlib
19+
. $STF_SUITE/tests/functional/cli_root/zpool_scrub/zpool_scrub.cfg
20+
21+
#
22+
# DESCRIPTION:
23+
# 'zpool scrub' with zfs_scrub_decompress set works
24+
#
25+
# STRATEGY:
26+
# 1 Set zfs_scrub_decompress tunable.
27+
# 2. Start a scrub and wait for it to finish.
28+
# 3. Repeat this with zfs_scrub_decompress not set.
29+
#
30+
31+
function cleanup
32+
{
33+
set_tunable32 SCRUB_DECOMPRESS 0
34+
zfs destroy $TESTPOOL/newfs
35+
}
36+
37+
verify_runnable "global"
38+
39+
log_onexit cleanup
40+
41+
log_assert "Scrub with decompression tunable set - works."
42+
43+
# Create out testing dataset
44+
log_must zfs create $TESTPOOL/newfs
45+
# Make sure compression is on
46+
log_must zfs set compression=on $TESTPOOL/newfs
47+
typeset file="/$TESTPOOL/newfs/$TESTFILE0"
48+
# Create some data in our dataset
49+
log_must dd if=/dev/urandom of=$file bs=1024 count=1024 oflag=sync
50+
# Make sure data is compressible
51+
log_must eval "echo 'aaaaaaaa' >> "$file
52+
53+
# Enable decompression of blocks read by scrub
54+
log_must set_tunable32 SCRUB_DECOMPRESS 1
55+
# Run and wait for scrub
56+
log_must zpool scrub -w $TESTPOOL
57+
58+
# Disable decompression of blocks read by scrub
59+
log_must set_tunable32 SCRUB_DECOMPRESS 0
60+
# Run and wait for scrub
61+
log_must zpool scrub -w $TESTPOOL
62+
63+
log_pass "Scrub with decompression tunable set - works."

0 commit comments

Comments
 (0)