From b048bfa9c15628c9ef8744befd892fecab20946f Mon Sep 17 00:00:00 2001 From: Don Brady Date: Sat, 24 May 2025 14:44:26 -0600 Subject: [PATCH] Allow opt-in of zvol blocks in special class Reviewed-by: Brian Behlendorf Reviewed-by: Alexander Motin Reviewed-by: Kjeld Schouten Signed-off-by: Don Brady Closes #14876 --- man/man7/zfsprops.7 | 4 +- man/man7/zpoolconcepts.7 | 5 +- module/zcommon/zfs_prop.c | 3 +- module/zfs/dmu.c | 3 +- module/zfs/spa_misc.c | 6 +- tests/runfiles/common.run | 3 +- tests/zfs-tests/tests/Makefile.am | 1 + .../alloc_class/alloc_class_016_pos.ksh | 60 +++++++++++++++++++ 8 files changed, 75 insertions(+), 10 deletions(-) create mode 100755 tests/zfs-tests/tests/functional/alloc_class/alloc_class_016_pos.ksh diff --git a/man/man7/zfsprops.7 b/man/man7/zfsprops.7 index b7b1047a8..ac58203f0 100644 --- a/man/man7/zfsprops.7 +++ b/man/man7/zfsprops.7 @@ -1281,12 +1281,12 @@ This feature must be enabled to be used .Pc . .It Sy special_small_blocks Ns = Ns Ar size This value represents the threshold block size for including small file -blocks into the special allocation class. +or zvol blocks into the special allocation class. Blocks smaller than or equal to this value will be assigned to the special allocation class while greater blocks will be assigned to the regular class. Valid values are zero or a power of two from 512 up to 1048576 (1 MiB). -The default size is 0 which means no small file blocks +The default size is 0 which means no small file or zvol blocks will be allocated in the special class. .Pp Before setting this property, a special class vdev must be added to the diff --git a/man/man7/zpoolconcepts.7 b/man/man7/zpoolconcepts.7 index 87feb6d41..60f16269a 100644 --- a/man/man7/zpoolconcepts.7 +++ b/man/man7/zpoolconcepts.7 @@ -488,7 +488,8 @@ current state of the pool won't be scanned during a scrub. Allocations in the special class are dedicated to specific block types. By default, this includes all metadata, the indirect blocks of user data, and any deduplication tables. -The class can also be provisioned to accept small file blocks. +The class can also be provisioned to accept small file blocks or zvol blocks +on a per dataset granularity. .Pp A pool must always have at least one normal .Pq non- Ns Sy dedup Ns /- Ns Sy special @@ -503,7 +504,7 @@ Deduplication tables can be excluded from the special class by unsetting the .Sy zfs_ddt_data_is_special ZFS module parameter. .Pp -Inclusion of small file blocks in the special class is opt-in. +Inclusion of small file or zvol blocks in the special class is opt-in. Each dataset can control the size of small file blocks allowed in the special class by setting the .Sy special_small_blocks diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 9d02b1f84..8b4c42517 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -737,7 +737,8 @@ zfs_prop_init(void) ZFS_TYPE_FILESYSTEM, "512 to 1M, power of 2", "RECSIZE", B_FALSE, sfeatures); zprop_register_number(ZFS_PROP_SPECIAL_SMALL_BLOCKS, - "special_small_blocks", 0, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, + "special_small_blocks", 0, PROP_INHERIT, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "zero or 512 to 1M, power of 2", "SPECIAL_SMALL_BLOCKS", B_FALSE, sfeatures); diff --git a/module/zfs/dmu.c b/module/zfs/dmu.c index 671c52ac0..f09b39628 100644 --- a/module/zfs/dmu.c +++ b/module/zfs/dmu.c @@ -2489,7 +2489,8 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp) memset(zp->zp_salt, 0, ZIO_DATA_SALT_LEN); memset(zp->zp_iv, 0, ZIO_DATA_IV_LEN); memset(zp->zp_mac, 0, ZIO_DATA_MAC_LEN); - zp->zp_zpl_smallblk = DMU_OT_IS_FILE(zp->zp_type) ? + zp->zp_zpl_smallblk = (DMU_OT_IS_FILE(zp->zp_type) || + zp->zp_type == DMU_OT_ZVOL) ? os->os_zpl_special_smallblock : 0; zp->zp_storage_type = dn ? dn->dn_storage_type : DMU_OT_NONE; diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c index f2b029778..a2cfee2de 100644 --- a/module/zfs/spa_misc.c +++ b/module/zfs/spa_misc.c @@ -2064,11 +2064,11 @@ spa_preferred_class(spa_t *spa, const zio_t *zio) } /* - * Allow small file blocks in special class in some cases (like - * for the dRAID vdev feature). But always leave a reserve of + * Allow small file or zvol blocks in special class if opted in by + * the special_smallblk property. However, always leave a reserve of * zfs_special_class_metadata_reserve_pct exclusively for metadata. */ - if (DMU_OT_IS_FILE(objtype) && + if ((DMU_OT_IS_FILE(objtype) || objtype == DMU_OT_ZVOL) && has_special_class && zio->io_size <= zp->zp_zpl_smallblk) { metaslab_class_t *special = spa_special_class(spa); uint64_t alloc = metaslab_class_get_alloc(special); diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index 49d019123..1711ecd41 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -37,7 +37,8 @@ tests = ['alloc_class_001_pos', 'alloc_class_002_neg', 'alloc_class_003_pos', 'alloc_class_004_pos', 'alloc_class_005_pos', 'alloc_class_006_pos', 'alloc_class_007_pos', 'alloc_class_008_pos', 'alloc_class_009_pos', 'alloc_class_010_pos', 'alloc_class_011_neg', 'alloc_class_012_pos', - 'alloc_class_013_pos', 'alloc_class_014_neg', 'alloc_class_015_pos'] + 'alloc_class_013_pos', 'alloc_class_014_neg', 'alloc_class_015_pos', + 'alloc_class_016_pos'] tags = ['functional', 'alloc_class'] [tests/functional/append] diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 1450c5bc8..5f52fd49a 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -429,6 +429,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/alloc_class/alloc_class_013_pos.ksh \ functional/alloc_class/alloc_class_014_neg.ksh \ functional/alloc_class/alloc_class_015_pos.ksh \ + functional/alloc_class/alloc_class_016_pos.ksh \ functional/alloc_class/cleanup.ksh \ functional/alloc_class/setup.ksh \ functional/append/file_append.ksh \ diff --git a/tests/zfs-tests/tests/functional/alloc_class/alloc_class_016_pos.ksh b/tests/zfs-tests/tests/functional/alloc_class/alloc_class_016_pos.ksh new file mode 100755 index 000000000..2cd3c2649 --- /dev/null +++ b/tests/zfs-tests/tests/functional/alloc_class/alloc_class_016_pos.ksh @@ -0,0 +1,60 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: CDDL-1.0 + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +. $STF_SUITE/tests/functional/alloc_class/alloc_class.kshlib + +# +# DESCRIPTION: +# File blocks and zvol blocks, where special_small_blocks is active, +# are expected to end up in the special class. +# + +verify_runnable "global" + +claim="File and zvol blocks using special_small_blocks end up in special class" + +log_assert $claim +log_onexit cleanup +log_must disk_setup + +log_must zpool create $TESTPOOL $ZPOOL_DISKS special $CLASS_DISK0 + +# Provision a filesystem with special_small_blocks and copy 10M to it +log_must zfs create -o compression=off -o special_small_blocks=32K \ + -o recordsize=32K $TESTPOOL/$TESTFS +log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/testfile bs=1M count=10 + +# Provision a volume with special_small_blocks and copy 10M to it +log_must zfs create -V 100M -b 32K -o special_small_blocks=32K \ + -o compression=off $TESTPOOL/$TESTVOL +block_device_wait "$ZVOL_DEVDIR/$TESTPOOL/$TESTVOL" +log_must dd if=/dev/urandom of=$ZVOL_DEVDIR/$TESTPOOL/$TESTVOL bs=1M count=10 + +sync_pool $TESTPOOL +zpool list -v $TESTPOOL + +# Get the amount allocated to special vdev using vdev 'allocated' property +result=$(zpool get -Hp allocated $TESTPOOL $CLASS_DISK0) +set -- $result +allocated=$3 +echo $allocated bytes allocated on special device $CLASS_DISK0 + +# Confirm that at least 20M was allocated +if [[ $allocated -lt 20971520 ]] +then + log_fail "$allocated on special vdev $CLASS_DISK0, but expecting 20M" +fi + +log_must zpool destroy -f "$TESTPOOL" +log_pass $claim