1#!/bin/ksh -p 2# SPDX-License-Identifier: CDDL-1.0 3# 4# CDDL HEADER START 5# 6# The contents of this file are subject to the terms of the 7# Common Development and Distribution License (the "License"). 8# You may not use this file except in compliance with the License. 9# 10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11# or https://opensource.org/licenses/CDDL-1.0. 12# See the License for the specific language governing permissions 13# and limitations under the License. 14# 15# When distributing Covered Code, include this CDDL HEADER in each 16# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17# If applicable, add the following below this CDDL HEADER, with the 18# fields enclosed by brackets "[]" replaced with your own identifying 19# information: Portions Copyright [yyyy] [name of copyright owner] 20# 21# CDDL HEADER END 22# 23 24# 25# Copyright (c) 2026 Klara, Inc. 26# 27 28. $STF_SUITE/include/libtest.shlib 29 30verify_runnable "global" 31 32# 33# DESCRIPTION: 34# 35# Several zpool properties exist to expose metaslab allocation class space 36# accounting. 37# 38# STRATEGY: 39# 1. Create a pool with raidz (to validate expansion and deflation metrics). 40# 2. For each allocation class: 41# - Add any required vdevs for this allocation class. 42# - Prepare a dataset configured to utilize this allocation class. 43# - Validate metrics reported by pool properties for allocation classes. 44# 3. For the whole pool, confirm that AVAIL, USABLE and USED report reasonable 45# values. 46# 47 48bs=128K 49count=100 50 51function writefile 52{ 53 dd if=/dev/urandom of=$1 bs=$bs count=$count 2>/dev/null 54} 55 56nfiles=5 57 58function writefiles # datadir [nfiles] 59{ 60 typeset datadir=$1 61 typeset -i n=${2:-$nfiles} 62 63 for i in {1..$n}; do 64 log_must writefile $TESTDIR/$datadir/file$i 65 done 66} 67 68pool=$TESTPOOL1 69 70function get_class_prop 71{ 72 get_pool_prop "class_${1}_${2}" $pool 73} 74 75# Wrapper for test to give more context to logs 76function check 77{ 78 shift 2 # class prop (test args) 79 test "$@" 80} 81 82function check_raidz_used # [mincap=1] 83{ 84 typeset -i mincap=${1:-1} 85 86 log_must check $class size $size -gt 0 87 log_must check $class capacity $cap -ge $mincap -a $cap -le 100 88 log_must check $class free $free -gt 0 -a $free -lt $size 89 log_must check $class allocated $alloc -gt 0 -a $alloc -lt $size 90 log_must check $class available $avail -gt 0 -a $avail -lt $free 91 log_must check $class usable $usable -gt 0 -a $usable -lt $size 92 log_must check $class used $used -gt 0 -a $used -lt $alloc 93 log_must check $class expandsize $expandsz = "-" 94 log_must check $class fragmentation $frag -lt 50 95} 96 97function check_raidz_unused 98{ 99 log_must check $class size $size -gt 0 100 log_must check $class capacity $cap -eq 0 101 log_must check $class free $free -eq $size 102 log_must check $class allocated $alloc -eq 0 103 log_must check $class available $avail -gt 0 -a $avail -lt $free 104 log_must check $class usable $usable -gt 0 -a $usable -lt $size 105 log_must check $class used $used -eq 0 106 log_must check $class expandsize $expandsz = "-" 107 log_must check $class fragmentation $frag -eq 0 108} 109 110function check_nonraidz_used # [mincap=1] 111{ 112 typeset -i mincap=${1:-1} 113 114 log_must check $class size $size -gt 0 115 log_must check $class capacity $cap -ge $mincap -a $cap -le 100 116 log_must check $class free $free -gt 0 -a $free -lt $size 117 log_must check $class allocated $alloc -gt 0 -a $alloc -lt $size 118 log_must check $class available $avail -eq $free 119 log_must check $class usable $usable -eq $size 120 log_must check $class used $used -eq $alloc 121 log_must check $class expandsize $expandsz = "-" 122 log_must check $class fragmentation $frag -lt 50 123} 124 125function check_nonraidz_unused 126{ 127 log_must check $class size $size -gt 0 128 log_must check $class capacity $cap -eq 0 129 log_must check $class free $free -eq $size 130 log_must check $class allocated $alloc -eq 0 131 log_must check $class available $avail -eq $free 132 log_must check $class usable $usable -eq $size 133 log_must check $class used $used -eq $alloc 134 log_must check $class expandsize $expandsz = "-" 135 log_must check $class fragmentation $frag -eq 0 136} 137 138# Log capacity tends to be >0% but <1% in these tests, so gets reported as 0. 139# Let that slide and rely on allocated/free checks for sanity, rather than 140# trying to tweak txg sync parameters to widen the race window. 141 142function check_raidz_log_used 143{ 144 check_raidz_used 0 145} 146 147function check_nonraidz_log_used 148{ 149 check_nonraidz_used 0 150} 151 152function check_unavailable 153{ 154 log_must check $class size $size -eq 0 155 log_must check $class capacity $cap -eq 0 156 log_must check $class free $free -eq 0 157 log_must check $class allocated $alloc -eq 0 158 log_must check $class available $avail -eq 0 159 log_must check $class usable $usable -eq 0 160 log_must check $class used $used -eq 0 161 log_must check $class expandsize $expandsz = "-" 162 log_must check $class fragmentation $frag = "-" 163} 164 165typeset -a classes=("normal" "special" "dedup" "log" "elog" "special_elog") 166 167normal_vdevs=$(seq -f $TEST_BASE_DIR/normal-vdev-%g 3) 168normal_vdev_size=$((1 << 30)) # 1 GiB 169 170special_vdevs=$(seq -f $TEST_BASE_DIR/special-vdev-%g 3) 171special_vdev_size=$((512 << 20)) # 512 MiB 172 173# Use a mirror for dedup to test expandsize. 174dedup_vdevs=$(seq -f $TEST_BASE_DIR/dedup-vdev-%g 2) 175dedup_vdev_size=$((256 << 20)) # 256 MiB 176 177# The log class can't be raided or expanded, so we only need one vdev. 178log_vdev="$TEST_BASE_DIR/log-vdev" 179log_vdev_size=$((128 << 20)) # 128 MiB 180 181embedded_slog_min_ms=$(get_tunable EMBEDDED_SLOG_MIN_MS) 182 183function cleanup 184{ 185 zpool destroy -f $pool 186 rm -f $normal_vdevs $normal_expand_vdev 187 rm -f $special_vdevs $special_expand_vdev 188 rm -f $dedup_vdevs $dedup_expand_vdev 189 rm -f $log_vdev 190 set_tunable32 EMBEDDED_SLOG_MIN_MS $embedded_slog_min_ms 191} 192log_onexit cleanup 193 194log_assert "zpool allocation class properties report metrics correctly" 195 196# Lower the threshold for provisioning embedded log metaslabs on small vdevs. 197log_must set_tunable32 EMBEDDED_SLOG_MIN_MS 8 198 199log_must truncate -s $normal_vdev_size $normal_vdevs 200log_must zpool create $pool \ 201 raidz $normal_vdevs 202log_must zfs set mountpoint=$TESTDIR $pool 203 204log_note "Normal Class" 205log_must zfs create \ 206 $pool/normal 207writefiles normal 208sync_pool $pool 209for class in "${classes[@]}"; do 210 typeset -il size=$(get_class_prop $class size) 211 typeset -i cap=$(get_class_prop $class capacity) 212 typeset -il free=$(get_class_prop $class free) 213 typeset -il alloc=$(get_class_prop $class allocated) 214 typeset -il avail=$(get_class_prop $class available) 215 typeset -il usable=$(get_class_prop $class usable) 216 typeset -il used=$(get_class_prop $class used) 217 typeset expandsz=$(get_class_prop $class expandsize) 218 typeset frag=$(get_class_prop $class fragmentation) 219 case $class in 220 normal) 221 check_raidz_used 222 ;; 223 elog) 224 check_raidz_unused 225 ;; 226 *) 227 check_unavailable 228 ;; 229 esac 230done 231 232log_note "Embedded Log Class" 233log_must zfs create \ 234 -o sync=always \ 235 $pool/elog 236writefiles elog 237for class in "${classes[@]}"; do 238 typeset -il size=$(get_class_prop $class size) 239 typeset -i cap=$(get_class_prop $class capacity) 240 typeset -il free=$(get_class_prop $class free) 241 typeset -il alloc=$(get_class_prop $class allocated) 242 typeset -il avail=$(get_class_prop $class available) 243 typeset -il usable=$(get_class_prop $class usable) 244 typeset -il used=$(get_class_prop $class used) 245 typeset expandsz=$(get_class_prop $class expandsize) 246 typeset frag=$(get_class_prop $class fragmentation) 247 case $class in 248 normal) 249 check_raidz_used 250 ;; 251 elog) 252 check_raidz_log_used 253 ;; 254 *) 255 check_unavailable 256 ;; 257 esac 258done 259 260log_note "Special Class" 261log_must truncate -s $special_vdev_size $special_vdevs 262log_must zpool add $pool \ 263 special raidz $special_vdevs 264log_must zfs create \ 265 -o recordsize=32K -o special_small_blocks=32K \ 266 $pool/special 267writefiles special 268sync_pool $pool 269for class in "${classes[@]}"; do 270 typeset -il size=$(get_class_prop $class size) 271 typeset -i cap=$(get_class_prop $class capacity) 272 typeset -il free=$(get_class_prop $class free) 273 typeset -il alloc=$(get_class_prop $class allocated) 274 typeset -il avail=$(get_class_prop $class available) 275 typeset -il usable=$(get_class_prop $class usable) 276 typeset -il used=$(get_class_prop $class used) 277 typeset expandsz=$(get_class_prop $class expandsize) 278 typeset frag=$(get_class_prop $class fragmentation) 279 case $class in 280 normal|special) 281 check_raidz_used 282 ;; 283 elog) 284 check_raidz_log_used 285 ;; 286 special_elog) 287 check_raidz_unused 288 ;; 289 *) 290 check_unavailable 291 ;; 292 esac 293done 294 295log_note "Special Embedded Log Class" 296log_must zfs create \ 297 -o recordsize=32K -o special_small_blocks=32K \ 298 -o sync=always \ 299 $pool/special_elog 300writefiles special_elog 301for class in "${classes[@]}"; do 302 typeset -il size=$(get_class_prop $class size) 303 typeset -i cap=$(get_class_prop $class capacity) 304 typeset -il free=$(get_class_prop $class free) 305 typeset -il alloc=$(get_class_prop $class allocated) 306 typeset -il avail=$(get_class_prop $class available) 307 typeset -il usable=$(get_class_prop $class usable) 308 typeset -il used=$(get_class_prop $class used) 309 typeset expandsz=$(get_class_prop $class expandsize) 310 typeset frag=$(get_class_prop $class fragmentation) 311 case $class in 312 normal|special) 313 check_raidz_used 314 ;; 315 elog|special_elog) 316 check_raidz_log_used 317 ;; 318 *) 319 check_unavailable 320 ;; 321 esac 322done 323 324log_note "Log Class" 325log_must truncate -s $log_vdev_size $log_vdev 326log_must zpool add $pool \ 327 log $log_vdev 328log_must zfs create \ 329 -o sync=always \ 330 $pool/log 331writefiles log 332for class in "${classes[@]}"; do 333 typeset -il size=$(get_class_prop $class size) 334 typeset -i cap=$(get_class_prop $class capacity) 335 typeset -il free=$(get_class_prop $class free) 336 typeset -il alloc=$(get_class_prop $class allocated) 337 typeset -il avail=$(get_class_prop $class available) 338 typeset -il usable=$(get_class_prop $class usable) 339 typeset -il used=$(get_class_prop $class used) 340 typeset expandsz=$(get_class_prop $class expandsize) 341 typeset frag=$(get_class_prop $class fragmentation) 342 case $class in 343 normal|special) 344 check_raidz_used 345 ;; 346 elog|special_elog) 347 check_raidz_log_used 348 ;; 349 log) 350 check_nonraidz_log_used 351 ;; 352 *) 353 check_unavailable 354 ;; 355 esac 356done 357 358log_note "Dedup Class" 359log_must truncate -s $dedup_vdev_size $dedup_vdevs 360log_must zpool add $pool \ 361 dedup mirror $dedup_vdevs 362log_must zfs create \ 363 -o dedup=on -o recordsize=4k \ 364 $pool/dedup 365writefiles dedup $((3 * nfiles)) 366sync_pool $pool 367for class in "${classes[@]}"; do 368 typeset -il size=$(get_class_prop $class size) 369 typeset -i cap=$(get_class_prop $class capacity) 370 typeset -il free=$(get_class_prop $class free) 371 typeset -il alloc=$(get_class_prop $class allocated) 372 typeset -il avail=$(get_class_prop $class available) 373 typeset -il usable=$(get_class_prop $class usable) 374 typeset -il used=$(get_class_prop $class used) 375 typeset expandsz=$(get_class_prop $class expandsize) 376 typeset frag=$(get_class_prop $class fragmentation) 377 case $class in 378 normal|special) 379 check_raidz_used 380 ;; 381 elog|special_elog) 382 check_raidz_log_used 383 ;; 384 log) 385 check_nonraidz_log_used 386 ;; 387 dedup) 388 check_nonraidz_used 389 ;; 390 *) 391 log_fail "unhandled class: $class" 392 ;; 393 esac 394done 395 396# Expansion 397typeset -il delta=$((32 << 20)) # 32 MiB 398log_must truncate -s $((dedup_vdev_size + delta)) $dedup_vdevs 399typeset -a vdevs=($dedup_vdevs) 400log_must zpool online -e $pool ${vdevs[0]} 401typeset -il size=$(get_class_prop dedup size) 402typeset -il expandsz=$(get_class_prop dedup expandsize) 403log_must test $expandsz -eq $delta 404 405# Pool-wide AVAIL/USABLE/USED 406typeset -il free=$(get_pool_prop free $pool) 407typeset -il avail=$(get_pool_prop available $pool) 408log_must test $avail -gt 0 -a $avail -lt $free 409typeset -il size=$(get_pool_prop size $pool) 410typeset -il usable=$(get_pool_prop usable $pool) 411log_must test $usable -gt 0 -a $usable -lt $size 412typeset -il alloc=$(get_pool_prop alloc $pool) 413typeset -il used=$(get_pool_prop used $pool) 414log_must test $used -gt 0 -a $used -lt $alloc 415 416cleanup 417