xref: /freebsd/usr.sbin/zonectl/tests/zonectl_test.sh (revision 48efb6c4eb9febc1c8c50d13a794e9165af71930)
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