1*48efb6c4Svoidanix# SPDX-License-Identifier: BSD-2-Clause 2*48efb6c4Svoidanix# 3*48efb6c4Svoidanix# Copyright (c) 2026 voidanix <voidanix@FreeBSD.org> 4*48efb6c4Svoidanix# 5*48efb6c4Svoidanix 6*48efb6c4SvoidanixMD_DEV_FILE="md.dev" 7*48efb6c4Svoidanix 8*48efb6c4Svoidanixalloc_zoned_md() 9*48efb6c4Svoidanix{ 10*48efb6c4Svoidanix atf_check truncate -s 1025m backing_file 11*48efb6c4Svoidanix md=$(mdconfig -a -t vnode -f backing_file) || \ 12*48efb6c4Svoidanix atf_fail "mdconfig -a failed" 13*48efb6c4Svoidanix echo ${md} > $MD_DEV_FILE 14*48efb6c4Svoidanix} 15*48efb6c4Svoidanix 16*48efb6c4Svoidanix# Print the number of zones matching a report option. 17*48efb6c4Svoidanixzone_count() 18*48efb6c4Svoidanix{ 19*48efb6c4Svoidanix zonectl -d /dev/${md}.zoned -c rz -o $1 -P summary | \ 20*48efb6c4Svoidanix awk '/zones,/ {print $1}' 21*48efb6c4Svoidanix} 22*48efb6c4Svoidanix 23*48efb6c4Svoidanixzoned_md_cleanup() 24*48efb6c4Svoidanix{ 25*48efb6c4Svoidanix if [ -f "$MD_DEV_FILE" ]; then 26*48efb6c4Svoidanix read md < $MD_DEV_FILE 27*48efb6c4Svoidanix [ -c /dev/${md}.zoned ] && \ 28*48efb6c4Svoidanix gzoned destroy ${md}.zoned 2>/dev/null 29*48efb6c4Svoidanix mdconfig -d -u ${md} 2>/dev/null 30*48efb6c4Svoidanix fi 31*48efb6c4Svoidanix true 32*48efb6c4Svoidanix} 33*48efb6c4Svoidanix 34*48efb6c4Svoidanixatf_test_case report_filter cleanup 35*48efb6c4Svoidanixreport_filter_head() 36*48efb6c4Svoidanix{ 37*48efb6c4Svoidanix atf_set "descr" "zonectl -o only reports zones matching the filter" 38*48efb6c4Svoidanix atf_set "require.user" "root" 39*48efb6c4Svoidanix} 40*48efb6c4Svoidanixreport_filter_body() 41*48efb6c4Svoidanix{ 42*48efb6c4Svoidanix alloc_zoned_md 43*48efb6c4Svoidanix atf_check gzoned create -s 256m -c 0-1 ${md} 44*48efb6c4Svoidanix 45*48efb6c4Svoidanix atf_check_equal "2" \ 46*48efb6c4Svoidanix "$(zonectl -d /dev/${md}.zoned -c rz -o nonwp -P summary | \ 47*48efb6c4Svoidanix awk '/zones,/ {print $1}')" 48*48efb6c4Svoidanix atf_check_equal "2" \ 49*48efb6c4Svoidanix "$(zonectl -d /dev/${md}.zoned -c rz -o empty -P summary | \ 50*48efb6c4Svoidanix awk '/zones,/ {print $1}')" 51*48efb6c4Svoidanix atf_check_equal "0" \ 52*48efb6c4Svoidanix "$(zonectl -d /dev/${md}.zoned -c rz -o full -P summary | \ 53*48efb6c4Svoidanix awk '/zones,/ {print $1}')" 54*48efb6c4Svoidanix} 55*48efb6c4Svoidanixreport_filter_cleanup() 56*48efb6c4Svoidanix{ 57*48efb6c4Svoidanix zoned_md_cleanup 58*48efb6c4Svoidanix} 59*48efb6c4Svoidanix 60*48efb6c4Svoidanixatf_test_case report_zones cleanup 61*48efb6c4Svoidanixreport_zones_head() 62*48efb6c4Svoidanix{ 63*48efb6c4Svoidanix atf_set "descr" "zonectl -c rz reports every zone" 64*48efb6c4Svoidanix atf_set "require.user" "root" 65*48efb6c4Svoidanix} 66*48efb6c4Svoidanixreport_zones_body() 67*48efb6c4Svoidanix{ 68*48efb6c4Svoidanix alloc_zoned_md 69*48efb6c4Svoidanix atf_check gzoned create -s 256m ${md} 70*48efb6c4Svoidanix atf_check -o match:"^4 zones," \ 71*48efb6c4Svoidanix zonectl -d /dev/${md}.zoned -c rz -P summary 72*48efb6c4Svoidanix atf_check_equal "4" \ 73*48efb6c4Svoidanix "$(zonectl -d /dev/${md}.zoned -c rz -P script | \ 74*48efb6c4Svoidanix awk 'END {print NR}')" 75*48efb6c4Svoidanix} 76*48efb6c4Svoidanixreport_zones_cleanup() 77*48efb6c4Svoidanix{ 78*48efb6c4Svoidanix zoned_md_cleanup 79*48efb6c4Svoidanix} 80*48efb6c4Svoidanix 81*48efb6c4Svoidanixatf_test_case report_starting_lba cleanup 82*48efb6c4Svoidanixreport_starting_lba_head() 83*48efb6c4Svoidanix{ 84*48efb6c4Svoidanix atf_set "descr" "zonectl -c rz -l reports zones from a starting LBA" 85*48efb6c4Svoidanix atf_set "require.user" "root" 86*48efb6c4Svoidanix} 87*48efb6c4Svoidanixreport_starting_lba_body() 88*48efb6c4Svoidanix{ 89*48efb6c4Svoidanix alloc_zoned_md 90*48efb6c4Svoidanix atf_check gzoned create -s 256m ${md} 91*48efb6c4Svoidanix # Only zones 2 and 3 lie at or past LBA 0x100000 (512m). 92*48efb6c4Svoidanix atf_check_equal "2" \ 93*48efb6c4Svoidanix "$(zonectl -d /dev/${md}.zoned -c rz -l 0x100000 -P summary | \ 94*48efb6c4Svoidanix awk '/zones,/ {print $1}')" 95*48efb6c4Svoidanix atf_check_equal "0x100000" \ 96*48efb6c4Svoidanix "$(zonectl -d /dev/${md}.zoned -c rz -l 0x100000 -P script | \ 97*48efb6c4Svoidanix awk -F',' 'NR == 1 {gsub(/ /, "", $1); print $1}')" 98*48efb6c4Svoidanix} 99*48efb6c4Svoidanixreport_starting_lba_cleanup() 100*48efb6c4Svoidanix{ 101*48efb6c4Svoidanix zoned_md_cleanup 102*48efb6c4Svoidanix} 103*48efb6c4Svoidanix 104*48efb6c4Svoidanixatf_test_case report_params cleanup 105*48efb6c4Svoidanixreport_params_head() 106*48efb6c4Svoidanix{ 107*48efb6c4Svoidanix atf_set "descr" "zonectl -c params reports the device parameters" 108*48efb6c4Svoidanix atf_set "require.user" "root" 109*48efb6c4Svoidanix} 110*48efb6c4Svoidanixreport_params_body() 111*48efb6c4Svoidanix{ 112*48efb6c4Svoidanix alloc_zoned_md 113*48efb6c4Svoidanix atf_check gzoned create -s 256m -m 3 ${md} 114*48efb6c4Svoidanix atf_check -o match:"Zone Mode: Host Managed" \ 115*48efb6c4Svoidanix -o match:"URSWRZ.*: Yes" \ 116*48efb6c4Svoidanix -o match:"Open Sequential Write Required Zones: 3" \ 117*48efb6c4Svoidanix zonectl -d /dev/${md}.zoned -c params 118*48efb6c4Svoidanix} 119*48efb6c4Svoidanixreport_params_cleanup() 120*48efb6c4Svoidanix{ 121*48efb6c4Svoidanix zoned_md_cleanup 122*48efb6c4Svoidanix} 123*48efb6c4Svoidanix 124*48efb6c4Svoidanixatf_test_case open_zone cleanup 125*48efb6c4Svoidanixopen_zone_head() 126*48efb6c4Svoidanix{ 127*48efb6c4Svoidanix atf_set "descr" "zonectl -c open explicitly opens a zone" 128*48efb6c4Svoidanix atf_set "require.user" "root" 129*48efb6c4Svoidanix} 130*48efb6c4Svoidanixopen_zone_body() 131*48efb6c4Svoidanix{ 132*48efb6c4Svoidanix alloc_zoned_md 133*48efb6c4Svoidanix atf_check gzoned create -s 256m ${md} 134*48efb6c4Svoidanix atf_check zonectl -d /dev/${md}.zoned -c open -l 0 135*48efb6c4Svoidanix atf_check_equal "1" "$(zone_count exp_open)" 136*48efb6c4Svoidanix} 137*48efb6c4Svoidanixopen_zone_cleanup() 138*48efb6c4Svoidanix{ 139*48efb6c4Svoidanix zoned_md_cleanup 140*48efb6c4Svoidanix} 141*48efb6c4Svoidanix 142*48efb6c4Svoidanixatf_test_case close_zone cleanup 143*48efb6c4Svoidanixclose_zone_head() 144*48efb6c4Svoidanix{ 145*48efb6c4Svoidanix atf_set "descr" "zonectl -c close closes an open zone" 146*48efb6c4Svoidanix atf_set "require.user" "root" 147*48efb6c4Svoidanix} 148*48efb6c4Svoidanixclose_zone_body() 149*48efb6c4Svoidanix{ 150*48efb6c4Svoidanix alloc_zoned_md 151*48efb6c4Svoidanix atf_check gzoned create -s 256m ${md} 152*48efb6c4Svoidanix atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1 153*48efb6c4Svoidanix atf_check_equal "1" "$(zone_count imp_open)" 154*48efb6c4Svoidanix atf_check zonectl -d /dev/${md}.zoned -c close -l 0 155*48efb6c4Svoidanix atf_check_equal "1" "$(zone_count closed)" 156*48efb6c4Svoidanix atf_check_equal "0" "$(zone_count imp_open)" 157*48efb6c4Svoidanix} 158*48efb6c4Svoidanixclose_zone_cleanup() 159*48efb6c4Svoidanix{ 160*48efb6c4Svoidanix zoned_md_cleanup 161*48efb6c4Svoidanix} 162*48efb6c4Svoidanix 163*48efb6c4Svoidanixatf_test_case finish_zone cleanup 164*48efb6c4Svoidanixfinish_zone_head() 165*48efb6c4Svoidanix{ 166*48efb6c4Svoidanix atf_set "descr" "zonectl -c finish transitions a zone to full" 167*48efb6c4Svoidanix atf_set "require.user" "root" 168*48efb6c4Svoidanix} 169*48efb6c4Svoidanixfinish_zone_body() 170*48efb6c4Svoidanix{ 171*48efb6c4Svoidanix alloc_zoned_md 172*48efb6c4Svoidanix atf_check gzoned create -s 256m ${md} 173*48efb6c4Svoidanix atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1 174*48efb6c4Svoidanix atf_check zonectl -d /dev/${md}.zoned -c finish -l 0 175*48efb6c4Svoidanix atf_check_equal "1" "$(zone_count full)" 176*48efb6c4Svoidanix} 177*48efb6c4Svoidanixfinish_zone_cleanup() 178*48efb6c4Svoidanix{ 179*48efb6c4Svoidanix zoned_md_cleanup 180*48efb6c4Svoidanix} 181*48efb6c4Svoidanix 182*48efb6c4Svoidanixatf_test_case zone_state_handling cleanup 183*48efb6c4Svoidanixzone_state_handling_head() 184*48efb6c4Svoidanix{ 185*48efb6c4Svoidanix atf_set "descr" \ 186*48efb6c4Svoidanix "zone commands follow ZBC spec outside their transition states" 187*48efb6c4Svoidanix atf_set "require.user" "root" 188*48efb6c4Svoidanix} 189*48efb6c4Svoidanixzone_state_handling_body() 190*48efb6c4Svoidanix{ 191*48efb6c4Svoidanix alloc_zoned_md 192*48efb6c4Svoidanix atf_check gzoned create -s 256m -c 0 ${md} 193*48efb6c4Svoidanix 194*48efb6c4Svoidanix # Explicitly targeting a conventional zone is the caller's error. 195*48efb6c4Svoidanix atf_check -s not-exit:0 -e ignore \ 196*48efb6c4Svoidanix zonectl -d /dev/${md}.zoned -c finish -l 0 197*48efb6c4Svoidanix # Closing an empty zone is a no-op. 198*48efb6c4Svoidanix atf_check zonectl -d /dev/${md}.zoned -c close -l 0x80000 199*48efb6c4Svoidanix atf_check_equal "3" "$(zone_count empty)" 200*48efb6c4Svoidanix # So is opening a full zone: it must stay full. 201*48efb6c4Svoidanix atf_check zonectl -d /dev/${md}.zoned -c finish -l 0x100000 202*48efb6c4Svoidanix atf_check zonectl -d /dev/${md}.zoned -c open -l 0x100000 203*48efb6c4Svoidanix atf_check_equal "1" "$(zone_count full)" 204*48efb6c4Svoidanix atf_check_equal "0" "$(zone_count exp_open)" 205*48efb6c4Svoidanix} 206*48efb6c4Svoidanixzone_state_handling_cleanup() 207*48efb6c4Svoidanix{ 208*48efb6c4Svoidanix zoned_md_cleanup 209*48efb6c4Svoidanix} 210*48efb6c4Svoidanix 211*48efb6c4Svoidanixatf_init_test_cases() 212*48efb6c4Svoidanix{ 213*48efb6c4Svoidanix atf_add_test_case report_filter 214*48efb6c4Svoidanix atf_add_test_case report_zones 215*48efb6c4Svoidanix atf_add_test_case report_starting_lba 216*48efb6c4Svoidanix atf_add_test_case report_params 217*48efb6c4Svoidanix atf_add_test_case open_zone 218*48efb6c4Svoidanix atf_add_test_case close_zone 219*48efb6c4Svoidanix atf_add_test_case finish_zone 220*48efb6c4Svoidanix atf_add_test_case zone_state_handling 221*48efb6c4Svoidanix} 222