xref: /freebsd/tests/sys/geom/class/zoned/zoned_test.sh (revision 48efb6c4eb9febc1c8c50d13a794e9165af71930)
1# SPDX-License-Identifier: BSD-2-Clause
2#
3# Copyright (c) 2026 voidanix <voidanix@FreeBSD.org>
4#
5
6. $(atf_get_srcdir)/conf.sh
7
8atf_test_case create cleanup
9create_head()
10{
11	atf_set "descr" "Basic gzoned device creation"
12	atf_set "require.user" "root"
13}
14create_body()
15{
16	gzoned_test_setup
17
18	zoned_backing_md
19	atf_check gzoned create -s 256m ${md}
20	atf_check test -c /dev/${md}.zoned
21}
22create_cleanup()
23{
24	gzoned_test_cleanup
25}
26
27atf_test_case is_zoned cleanup
28is_zoned_head()
29{
30	atf_set "descr" "gzoned creates a host-managed zoned device"
31	atf_set "require.user" "root"
32}
33is_zoned_body()
34{
35	gzoned_test_setup
36
37	zoned_backing_md
38	atf_check gzoned create ${md}
39	atf_check_equal "Host Managed" \
40	    "$(zonectl -d /dev/${md}.zoned -c params | \
41	    awk -F': ' '/Zone Mode/ {print $2}')"
42}
43is_zoned_cleanup()
44{
45	gzoned_test_cleanup
46}
47
48atf_test_case conventional cleanup
49conventional_head()
50{
51	atf_set "descr" "gzoned device has a few conventional zones"
52	atf_set "require.user" "root"
53}
54conventional_body()
55{
56	gzoned_test_setup
57
58	zoned_backing_md
59	atf_check gzoned create -s 256m -c 1,3 ${md}
60	atf_check_equal "2" "$(zone_count nonwp)"
61}
62conventional_cleanup()
63{
64	gzoned_test_cleanup
65}
66
67atf_test_case conventional_single cleanup
68conventional_single_head()
69{
70	atf_set "descr" "gzoned create -c accepts a single zone"
71	atf_set "require.user" "root"
72}
73conventional_single_body()
74{
75	gzoned_test_setup
76
77	zoned_backing_md
78	atf_check gzoned create -s 256m -c 2 ${md}
79	atf_check_equal "1" "$(zone_count nonwp)"
80	# The conventional zone is zone 2. At LBA 512m / 512 = 0x100000.
81	atf_check_equal "0x100000" \
82	    "$(zonectl -d /dev/${md}.zoned -c rz -o nonwp -P script | \
83	    awk -F',' 'NR == 1 {gsub(/ /, "", $1); print $1}')"
84}
85conventional_single_cleanup()
86{
87	gzoned_test_cleanup
88}
89
90atf_test_case conventional_range cleanup
91conventional_range_head()
92{
93	atf_set "descr" "gzoned device has a range of conventional zones"
94	atf_set "require.user" "root"
95}
96conventional_range_body()
97{
98	gzoned_test_setup
99
100	zoned_backing_md
101	atf_check gzoned create -s 256m -c 0-2 ${md}
102	atf_check_equal "3" "$(zone_count nonwp)"
103}
104conventional_range_cleanup()
105{
106	gzoned_test_cleanup
107}
108
109atf_test_case all_zones_same cleanup
110all_zones_same_head()
111{
112	atf_set "descr" "gzoned device has all zones of the same type"
113	atf_set "require.user" "root"
114}
115all_zones_same_body()
116{
117	gzoned_test_setup
118
119	zoned_backing_md
120	atf_check gzoned create -s 256m -c 0-3 ${md}
121	atf_check -o match:"^4 zones," \
122	    -o match:"Zone lengths and types are all the same" \
123	    zonectl -d /dev/${md}.zoned -c rz -P summary
124}
125all_zones_same_cleanup()
126{
127	gzoned_test_cleanup
128}
129
130atf_test_case conventional_ranges cleanup
131conventional_ranges_head()
132{
133	atf_set "descr" "gzoned create -c accepts a list of zone ranges"
134	atf_set "require.user" "root"
135}
136conventional_ranges_body()
137{
138	gzoned_test_setup
139
140	zoned_backing_md
141	atf_check gzoned create -s 256m -c 0,2-3 ${md}
142	atf_check_equal "3" "$(zone_count nonwp)"
143	# Zone 1 alone stays sequential: the second conventional zone
144	# reported must be zone 2, at LBA 512m / 512 = 0x100000.
145	atf_check_equal "0x100000" \
146	    "$(zonectl -d /dev/${md}.zoned -c rz -o nonwp -P script | \
147	    awk -F',' 'NR == 2 {gsub(/ /, "", $1); print $1}')"
148}
149conventional_ranges_cleanup()
150{
151	gzoned_test_cleanup
152}
153
154atf_test_case zone_size cleanup
155zone_size_head()
156{
157	atf_set "descr" "gzoned create -s adjusts the zone size"
158	atf_set "require.user" "root"
159}
160zone_size_body()
161{
162	gzoned_test_setup
163
164	zoned_backing_md
165	atf_check gzoned create -s 128m ${md}
166	# 1024m of zone space now holds eight 128m zones instead of four.
167	atf_check -o match:"^8 zones," \
168	    zonectl -d /dev/${md}.zoned -c rz -P summary
169	# Zone 1 starts at LBA 128m / 512 = 0x40000.
170	atf_check_equal "0x40000" "$(zone_start 1)"
171}
172zone_size_cleanup()
173{
174	gzoned_test_cleanup
175}
176
177atf_test_case zone_count_derived cleanup
178zone_count_derived_head()
179{
180	atf_set "descr" "gzoned obtains the same zone count at every taste"
181	atf_set "require.user" "root"
182}
183zone_count_derived_body()
184{
185	gzoned_test_setup
186
187	zoned_backing_md
188	atf_check gzoned create -s 128m ${md}
189	atf_check -o match:"^8 zones," \
190	    zonectl -d /dev/${md}.zoned -c rz -P summary
191	last_start=$(zone_start 7)
192
193	atf_check gzoned stop ${md}.zoned
194	wait_dev_gone /dev/${md}.zoned
195	true > /dev/${md}
196	wait_dev /dev/${md}.zoned
197	atf_check -o match:"^8 zones," \
198	    zonectl -d /dev/${md}.zoned -c rz -P summary
199	atf_check_equal "${last_start}" "$(zone_start 7)"
200}
201zone_count_derived_cleanup()
202{
203	gzoned_test_cleanup
204}
205
206atf_test_case create_readonly cleanup
207create_readonly_head()
208{
209	atf_set "descr" "gzoned refuses a provider it cannot write to"
210	atf_set "require.user" "root"
211}
212create_readonly_body()
213{
214	gzoned_test_setup
215
216	atf_check truncate -s 1025m backing_file
217	attach_md md -t vnode -f backing_file -o readonly
218	atf_check -s not-exit:0 -e ignore gzoned create -s 256m ${md}
219	atf_check test ! -c /dev/${md}.zoned
220}
221create_readonly_cleanup()
222{
223	gzoned_test_cleanup
224}
225
226atf_test_case zone_size_too_large cleanup
227zone_size_too_large_head()
228{
229	atf_set "descr" "gzoned refuses a zone size larger than the provider"
230	atf_set "require.user" "root"
231}
232zone_size_too_large_body()
233{
234	gzoned_test_setup
235
236	zoned_backing_md
237	atf_check -s not-exit:0 -e match:"unusable" \
238	    gzoned create -s 2g ${md}
239	atf_check test ! -c /dev/${md}.zoned
240}
241zone_size_too_large_cleanup()
242{
243	gzoned_test_cleanup
244}
245
246atf_test_case zone_size_too_many cleanup
247zone_size_too_many_head()
248{
249	atf_set "descr" "gzoned refuses a zone size yielding too many zones"
250	atf_set "require.user" "root"
251}
252zone_size_too_many_body()
253{
254	gzoned_test_setup
255
256	# One zone per sector on a sparse 8T provider overflows the 32-bit zone
257	# count.
258	atf_check truncate -s 8t backing_file
259	attach_md md -t vnode -f backing_file
260	atf_check -s not-exit:0 -e match:"unusable" \
261	    gzoned create -s 512 ${md}
262	atf_check test ! -c /dev/${md}.zoned
263}
264zone_size_too_many_cleanup()
265{
266	gzoned_test_cleanup
267}
268
269atf_test_case reset_wp cleanup
270reset_wp_head()
271{
272	atf_set "descr" \
273	    "gzoned write pointer can be reset inside a seq-write-req zone"
274	atf_set "require.user" "root"
275}
276reset_wp_body()
277{
278	gzoned_test_setup
279
280	zoned_backing_md
281	atf_check gzoned create -s 256m ${md}
282
283	# Advance the write pointer of zone 0 by 1m (2048 512-byte LBAs).
284	atf_check -e ignore \
285	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
286	atf_check_equal "0x800" "$(zone_wp 0)"
287
288	atf_check zonectl -d /dev/${md}.zoned -c rwp -l 0
289	atf_check_equal "0" "$(zone_wp 0)"
290}
291reset_wp_cleanup()
292{
293	gzoned_test_cleanup
294}
295
296atf_test_case reset_wp_conv cleanup
297reset_wp_conv_head()
298{
299	atf_set "descr" \
300	    "gzoned write pointer cannot be reset inside a conventional zone"
301	atf_set "require.user" "root"
302}
303reset_wp_conv_body()
304{
305	gzoned_test_setup
306
307	zoned_backing_md
308	atf_check gzoned create -s 256m -c 0 ${md}
309	atf_check test -c /dev/${md}.zoned
310	atf_check_equal "1" "$(zone_count nonwp)"
311	atf_check -s not-exit:0 -e ignore \
312	    zonectl -d /dev/${md}.zoned -c rwp -l 0
313}
314reset_wp_conv_cleanup()
315{
316	gzoned_test_cleanup
317}
318
319atf_test_case reset_wp_empty cleanup
320reset_wp_empty_head()
321{
322	atf_set "descr" \
323	    "gzoned accepts a write pointer reset of an already-empty zone"
324	atf_set "require.user" "root"
325}
326reset_wp_empty_body()
327{
328	gzoned_test_setup
329
330	zoned_backing_md
331	atf_check gzoned create -s 256m ${md}
332	atf_check_equal "0" "$(zone_wp 0)"
333	atf_check zonectl -d /dev/${md}.zoned -c rwp -l 0
334	atf_check_equal "0" "$(zone_wp 0)"
335	atf_check_equal "4" "$(zone_count empty)"
336}
337reset_wp_empty_cleanup()
338{
339	gzoned_test_cleanup
340}
341
342atf_test_case reset_wp_finished cleanup
343reset_wp_finished_head()
344{
345	atf_set "descr" "gzoned resets a finished zone back to empty"
346	atf_set "require.user" "root"
347}
348reset_wp_finished_body()
349{
350	gzoned_test_setup
351
352	zoned_backing_md
353	atf_check gzoned create -s 256m ${md}
354	atf_check zonectl -d /dev/${md}.zoned -c finish -l 0
355	atf_check_equal "1" "$(zone_count full)"
356
357	atf_check zonectl -d /dev/${md}.zoned -c rwp -l 0
358	atf_check_equal "0" "$(zone_count full)"
359	atf_check_equal "0" "$(zone_wp 0)"
360	atf_check_equal "4" "$(zone_count empty)"
361}
362reset_wp_finished_cleanup()
363{
364	gzoned_test_cleanup
365}
366
367atf_test_case reset_wp_all_readonly cleanup
368reset_wp_all_readonly_head()
369{
370	atf_set "descr" \
371	    "gzoned keeps the contents of a zone reset all did not reset"
372	atf_set "require.user" "root"
373}
374reset_wp_all_readonly_body()
375{
376	gzoned_test_setup
377
378	zoned_backing_md
379	atf_check gzoned create -s 256m ${md}
380
381	# Fill the start of zone 1 with something unrecognizable.
382	atf_check -e ignore dd if=/dev/random of=pattern bs=1m count=1
383	atf_check -e ignore \
384	    dd if=pattern of=/dev/${md}.zoned bs=1m oseek=256 count=1
385	atf_check gzoned fault -z 1 -s ro ${md}.zoned
386
387	atf_check zonectl -d /dev/${md}.zoned -c rwp -a
388	atf_check_equal "1" "$(zone_count ro)"
389	# Read-only zone reports no write pointer.  Clear the fault to see
390	# whether the pointer was left where it was beforehand.
391	atf_check gzoned fault -z 1 -s clear ${md}.zoned
392	atf_check_equal "0x80800" "$(zone_wp 524288)"
393	atf_check -e ignore \
394	    dd if=/dev/${md}.zoned of=after bs=1m iseek=256 count=1
395	atf_check cmp pattern after
396}
397reset_wp_all_readonly_cleanup()
398{
399	gzoned_test_cleanup
400}
401
402atf_test_case zone_id_lowest_lba cleanup
403zone_id_lowest_lba_head()
404{
405	atf_set "descr" \
406	    "gzoned only takes a zone ID that is a zone's lowest LBA"
407	atf_set "require.user" "root"
408}
409zone_id_lowest_lba_body()
410{
411	gzoned_test_setup
412
413	zoned_backing_md
414	atf_check gzoned create -s 256m ${md}
415
416	atf_check zonectl -d /dev/${md}.zoned -c rwp -l 0x80000
417	atf_check -s not-exit:0 -e ignore \
418	    zonectl -d /dev/${md}.zoned -c rwp -l 0x80001
419	atf_check -s not-exit:0 -e ignore \
420	    zonectl -d /dev/${md}.zoned -c open -l 1
421	atf_check -s not-exit:0 -e ignore \
422	    zonectl -d /dev/${md}.zoned -c finish -l 0x7ffff
423	atf_check_equal "4" "$(zone_count empty)"
424
425	atf_check zonectl -d /dev/${md}.zoned -c rwp -a
426}
427zone_id_lowest_lba_cleanup()
428{
429	gzoned_test_cleanup
430}
431
432atf_test_case unrestricted_reads cleanup
433unrestricted_reads_head()
434{
435	atf_set "descr" \
436	    "gzoned device allows reads above the write pointer by default"
437	atf_set "require.user" "root"
438}
439unrestricted_reads_body()
440{
441	gzoned_test_setup
442
443	zoned_backing_md
444	atf_check gzoned create -s 256m ${md}
445	atf_check -o match:"URSWRZ.*: Yes" \
446	    zonectl -d /dev/${md}.zoned -c params
447	# Reading an empty sequential zone is fine.
448	atf_check -e ignore \
449	    dd if=/dev/${md}.zoned of=/dev/null bs=512 count=1
450	# So is one 768k read straddling the 256m mark, as in write_boundary:
451	# spanning zones of the same type is what URSWRZ lifts.
452	atf_check -e ignore \
453	    dd if=/dev/${md}.zoned of=/dev/null bs=768k iseek=341 count=1
454}
455unrestricted_reads_cleanup()
456{
457	gzoned_test_cleanup
458}
459
460atf_test_case read_zone_types cleanup
461read_zone_types_head()
462{
463	atf_set "descr" \
464	    "Reads may not cross between zone types even when unrestricted"
465	atf_set "require.user" "root"
466}
467read_zone_types_body()
468{
469	gzoned_test_setup
470
471	zoned_backing_md
472	atf_check gzoned create -s 256m -c 0 ${md}
473	atf_check -o match:"URSWRZ.*: Yes" \
474	    zonectl -d /dev/${md}.zoned -c params
475
476	# The 768k read straddling the 256m mark reaches the kernel as one
477	# bio, running from a conventional zone into the sequential.
478	atf_check -s not-exit:0 -e ignore \
479	    dd if=/dev/${md}.zoned of=/dev/null bs=768k iseek=341 count=1
480	# Preceding block lies within the conventional zone: it's fine.
481	atf_check -e ignore \
482	    dd if=/dev/${md}.zoned of=/dev/null bs=768k iseek=340 count=1
483}
484read_zone_types_cleanup()
485{
486	gzoned_test_cleanup
487}
488
489atf_test_case restricted_reads cleanup
490restricted_reads_head()
491{
492	atf_set "descr" \
493	    "gzoned -u device rejects reads beyond the write pointer"
494	atf_set "require.user" "root"
495}
496restricted_reads_body()
497{
498	gzoned_test_setup
499
500	zoned_backing_md
501	atf_check gzoned create -s 256m -u ${md}
502	atf_check -o match:"URSWRZ.*: No" \
503	    zonectl -d /dev/${md}.zoned -c params
504
505	# Reading an empty sequential zone must fail...
506	atf_check -s not-exit:0 -e ignore \
507	    dd if=/dev/${md}.zoned of=/dev/null bs=512 count=1
508
509	# ...until data has been written.
510	atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
511	atf_check -e ignore \
512	    dd if=/dev/${md}.zoned of=/dev/null bs=1m count=1
513	atf_check -s not-exit:0 -e ignore \
514	    dd if=/dev/${md}.zoned of=/dev/null bs=1m count=2
515}
516restricted_reads_cleanup()
517{
518	gzoned_test_cleanup
519}
520
521atf_test_case write_at_wp cleanup
522write_at_wp_head()
523{
524	atf_set "descr" "gzoned accepts writes at the write pointer"
525	atf_set "require.user" "root"
526}
527write_at_wp_body()
528{
529	gzoned_test_setup
530
531	zoned_backing_md
532	atf_check gzoned create -s 256m ${md}
533	atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
534	atf_check_equal "0x800" "$(zone_wp 0)"
535	# A second write at the advanced write pointer is accepted too.
536	atf_check -e ignore \
537	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m oseek=1 count=1
538	atf_check_equal "0x1000" "$(zone_wp 0)"
539}
540write_at_wp_cleanup()
541{
542	gzoned_test_cleanup
543}
544
545atf_test_case write_out_of_order cleanup
546write_out_of_order_head()
547{
548	atf_set "descr" "gzoned rejects writes that skip the write pointer"
549	atf_set "require.user" "root"
550}
551write_out_of_order_body()
552{
553	gzoned_test_setup
554
555	zoned_backing_md
556	atf_check gzoned create -s 256m ${md}
557	# Zone 0 write pointer is at LBA 0; writing at 1m shall fail.
558	atf_check -s not-exit:0 -e ignore \
559	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m oseek=1 count=1
560	atf_check_equal "0" "$(zone_wp 0)"
561}
562write_out_of_order_cleanup()
563{
564	gzoned_test_cleanup
565}
566
567atf_test_case write_boundary cleanup
568write_boundary_head()
569{
570	atf_set "descr" "gzoned rejects writes crossing a zone boundary"
571	atf_set "require.user" "root"
572}
573write_boundary_body()
574{
575	gzoned_test_setup
576
577	zoned_backing_md
578	atf_check gzoned create -s 256m -c 0 ${md}
579	# One 768k write straddling the 256m mark (768k does not divide
580	# 256m, so the request reaches the kernel as a single bio going
581	# from the conventional zone 0 into the sequential zone 1).
582	atf_check -s not-exit:0 -e ignore \
583	    dd if=/dev/zero of=/dev/${md}.zoned bs=768k oseek=341 count=1
584	# The preceding block lies within the conventional zone and is fine.
585	atf_check -e ignore \
586	    dd if=/dev/zero of=/dev/${md}.zoned bs=768k oseek=340 count=1
587}
588write_boundary_cleanup()
589{
590	gzoned_test_cleanup
591}
592
593atf_test_case write_conv_boundary cleanup
594write_conv_boundary_head()
595{
596	atf_set "descr" \
597	    "gzoned allows writes crossing two conventional zones"
598	atf_set "require.user" "root"
599}
600write_conv_boundary_body()
601{
602	gzoned_test_setup
603
604	zoned_backing_md
605	atf_check gzoned create -s 256m -c 0-1 ${md}
606	# The same straddling write rejected in write_boundary is fine
607	# when the zones on both sides are conventional.
608	atf_check -e ignore \
609	    dd if=/dev/zero of=/dev/${md}.zoned bs=768k oseek=341 count=1
610}
611write_conv_boundary_cleanup()
612{
613	gzoned_test_cleanup
614}
615
616atf_test_case write_conv_anywhere cleanup
617write_conv_anywhere_head()
618{
619	atf_set "descr" "gzoned conventional zones accept writes at any offset"
620	atf_set "require.user" "root"
621}
622write_conv_anywhere_body()
623{
624	gzoned_test_setup
625
626	zoned_backing_md
627	atf_check gzoned create -s 256m -c 0 ${md}
628	# No write pointer: writing far into the zone and then again before
629	# the previous write must both succeed, and the zone never opens.
630	atf_check -e ignore \
631	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m oseek=10 count=1
632	atf_check -e ignore \
633	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m oseek=2 count=1
634	atf_check_equal "0" "$(zone_count imp_open)"
635}
636write_conv_anywhere_cleanup()
637{
638	gzoned_test_cleanup
639}
640
641atf_test_case write_full cleanup
642write_full_head()
643{
644	atf_set "descr" "gzoned rejects writes to a full zone"
645	atf_set "require.user" "root"
646}
647write_full_body()
648{
649	gzoned_test_setup
650
651	zoned_backing_md
652	atf_check gzoned create -s 256m ${md}
653	atf_check zonectl -d /dev/${md}.zoned -c finish -l 0
654	atf_check_equal "1" "$(zone_count full)"
655	atf_check -s not-exit:0 -e ignore \
656	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
657}
658write_full_cleanup()
659{
660	gzoned_test_cleanup
661}
662
663atf_test_case write_closed_zone cleanup
664write_closed_zone_head()
665{
666	atf_set "descr" "gzoned writes to a closed zone implicitly reopen it"
667	atf_set "require.user" "root"
668}
669write_closed_zone_body()
670{
671	gzoned_test_setup
672
673	zoned_backing_md
674	atf_check gzoned create -s 256m ${md}
675	atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
676	atf_check zonectl -d /dev/${md}.zoned -c close -l 0
677	atf_check_equal "1" "$(zone_count closed)"
678
679	atf_check -e ignore \
680	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m oseek=1 count=1
681	atf_check_equal "0" "$(zone_count closed)"
682	atf_check_equal "1" "$(zone_count imp_open)"
683}
684write_closed_zone_cleanup()
685{
686	gzoned_test_cleanup
687}
688
689atf_test_case persistence cleanup
690persistence_head()
691{
692	atf_set "descr" "gzoned zone state survives a stop and re-taste"
693	atf_set "require.user" "root"
694}
695persistence_body()
696{
697	gzoned_test_setup
698
699	zoned_backing_md
700	atf_check gzoned create -s 256m ${md}
701	atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
702	atf_check_equal "0x800" "$(zone_wp 0)"
703
704	# Stopping will commit the zone state; re-tasting the backing provider
705	# brings the device back with its write pointers restored.
706	atf_check gzoned stop ${md}.zoned
707	wait_dev_gone /dev/${md}.zoned
708	true > /dev/${md}
709	wait_dev /dev/${md}.zoned
710	atf_check_equal "0x800" "$(zone_wp 0)"
711}
712persistence_cleanup()
713{
714	gzoned_test_cleanup
715}
716
717atf_test_case clear_clears_metadata cleanup
718clear_clears_metadata_head()
719{
720	atf_set "descr" "gzoned clear erases the on-disk metadata"
721	atf_set "require.user" "root"
722}
723clear_clears_metadata_body()
724{
725	gzoned_test_setup
726
727	zoned_backing_md
728	atf_check gzoned create -s 256m ${md}
729	# The backing provider is held open for as long as the device exists,
730	# so its metadata can only be cleared once the device is gone.
731	atf_check -s not-exit:0 -e ignore gzoned clear /dev/${md}
732	atf_check gzoned stop ${md}.zoned
733	wait_dev_gone /dev/${md}.zoned
734	atf_check gzoned clear /dev/${md}
735	true > /dev/${md}
736	sleep 1
737	atf_check test ! -c /dev/${md}.zoned
738}
739clear_clears_metadata_cleanup()
740{
741	gzoned_test_cleanup
742}
743
744atf_test_case max_open cleanup
745max_open_head()
746{
747	atf_set "descr" "gzoned enforces the open-zone limit"
748	atf_set "require.user" "root"
749}
750max_open_body()
751{
752	gzoned_test_setup
753
754	zoned_backing_md
755	atf_check gzoned create -s 256m -m 1 ${md}
756	atf_check -o match:"Open Sequential Write Required Zones: 1" \
757	    zonectl -d /dev/${md}.zoned -c params
758
759	# A write into a second zone implicitly closes the first.
760	atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
761	atf_check -e ignore \
762	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m oseek=256 count=1
763	atf_check_equal "1" "$(zone_count closed)"
764	atf_check_equal "1" "$(zone_count imp_open)"
765
766	# Explicitly opening zone 0 closes the implicitly open zone 1; a second
767	# explicit open will exceed the limit.
768	atf_check zonectl -d /dev/${md}.zoned -c open -l 0
769	atf_check_equal "1" "$(zone_count exp_open)"
770	atf_check -s not-exit:0 -e ignore \
771	    zonectl -d /dev/${md}.zoned -c open -l 0x80000
772}
773max_open_cleanup()
774{
775	gzoned_test_cleanup
776}
777
778atf_test_case max_open_limit cleanup
779max_open_limit_head()
780{
781	atf_set "descr" "gzoned rejects explicit opens beyond the open limit"
782	atf_set "require.user" "root"
783}
784max_open_limit_body()
785{
786	gzoned_test_setup
787
788	zoned_backing_md
789	atf_check gzoned create -s 256m -m 2 ${md}
790	atf_check zonectl -d /dev/${md}.zoned -c open -l 0
791	atf_check zonectl -d /dev/${md}.zoned -c open -l 0x80000
792	atf_check_equal "2" "$(zone_count exp_open)"
793
794	# Explicitly open zones cannot be implicitly closed. A third open
795	# exceeds the limit until one of them is closed.
796	atf_check -s not-exit:0 -e ignore \
797	    zonectl -d /dev/${md}.zoned -c open -l 0x100000
798	atf_check zonectl -d /dev/${md}.zoned -c close -l 0
799	atf_check zonectl -d /dev/${md}.zoned -c open -l 0x100000
800	atf_check_equal "2" "$(zone_count exp_open)"
801}
802max_open_limit_cleanup()
803{
804	gzoned_test_cleanup
805}
806
807atf_test_case stop_force cleanup
808stop_force_head()
809{
810	atf_set "descr" "gzoned stop -f stops a busy device"
811	atf_set "require.user" "root"
812}
813stop_force_body()
814{
815	gzoned_test_setup
816
817	zoned_backing_md
818	atf_check gzoned create -s 256m ${md}
819
820	# Keep the device open; a plain stop must fail, a forced one work.
821	sleep 5 < /dev/${md}.zoned &
822	spid=$!
823	sleep 0.5
824	atf_check -s not-exit:0 -e ignore gzoned stop ${md}.zoned
825	atf_check gzoned stop -f ${md}.zoned
826	wait_dev_gone /dev/${md}.zoned
827	kill $spid 2>/dev/null
828	wait $spid 2>/dev/null || true
829}
830stop_force_cleanup()
831{
832	gzoned_test_cleanup
833}
834
835atf_test_case create_on_zoned cleanup
836create_on_zoned_head()
837{
838	atf_set "descr" \
839	    "gzoned create on an already-zoned device fails gracefully"
840	atf_set "require.user" "root"
841}
842create_on_zoned_body()
843{
844	gzoned_test_setup
845
846	zoned_backing_md
847	atf_check gzoned create -s 256m ${md}
848	# The metadata of the second device cannot land in the sequential zones
849	# of the first; creation must be refused and leave the first device
850	# intact, with all zones still empty.
851	atf_check -s not-exit:0 -e match:"host-managed" \
852	    gzoned create -s 256m ${md}.zoned
853	atf_check test -c /dev/${md}.zoned
854	atf_check -o match:"^4 zones," \
855	    zonectl -d /dev/${md}.zoned -c rz -P summary
856	atf_check_equal "4" "$(zone_count empty)"
857}
858create_on_zoned_cleanup()
859{
860	gzoned_test_cleanup
861}
862
863atf_test_case fault_injection cleanup
864fault_injection_head()
865{
866	atf_set "descr" "gzoned faulted zones reject writes until cleared"
867	atf_set "require.user" "root"
868}
869fault_injection_body()
870{
871	gzoned_test_setup
872
873	zoned_backing_md
874	atf_check gzoned create -s 256m ${md}
875
876	atf_check gzoned fault -z 1 -s ro ${md}.zoned
877	atf_check_equal "1" "$(zone_count ro)"
878	atf_check -s not-exit:0 -e ignore \
879	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m oseek=256 count=1
880
881	atf_check gzoned fault -z 1 -s clear ${md}.zoned
882	atf_check_equal "0" "$(zone_count ro)"
883	atf_check -e ignore \
884	    dd if=/dev/zero of=/dev/${md}.zoned bs=1m oseek=256 count=1
885
886	atf_check gzoned fault -z 2 -s offline ${md}.zoned
887	atf_check_equal "1" "$(zone_count offline)"
888	# A read only zone stays readable; an offline one does not.
889	atf_check gzoned fault -z 1 -s ro ${md}.zoned
890	atf_check -e ignore \
891	    dd if=/dev/${md}.zoned of=/dev/null bs=1m iseek=256 count=1
892	atf_check -s not-exit:0 -e ignore \
893	    dd if=/dev/${md}.zoned of=/dev/null bs=1m iseek=512 count=1
894	atf_check gzoned fault -z 1 -s clear ${md}.zoned
895	atf_check gzoned fault -z 3 -s reset ${md}.zoned
896	atf_check_equal "1" "$(zone_count reset)"
897}
898fault_injection_cleanup()
899{
900	gzoned_test_cleanup
901}
902
903atf_test_case reset_wp_clears_fault cleanup
904reset_wp_clears_fault_head()
905{
906	atf_set "descr" "gzoned reset write pointer clears an injected reset fault"
907	atf_set "require.user" "root"
908}
909reset_wp_clears_fault_body()
910{
911	gzoned_test_setup
912
913	zoned_attach_md
914
915	# Freshly created zone is already empty: the injected fault is the only
916	# thing a reset has to clear.
917	atf_check gzoned fault -z 3 -s reset ${md}.zoned
918	atf_check_equal "1" "$(zone_count reset)"
919	atf_check zonectl -d /dev/${md}.zoned -c rwp -l $(zone_start 3)
920	atf_check_equal "0" "$(zone_count reset)"
921
922	# ALso holds when every zone is reset at once.
923	atf_check gzoned fault -z 3 -s reset ${md}.zoned
924	atf_check_equal "1" "$(zone_count reset)"
925	atf_check zonectl -d /dev/${md}.zoned -c rwp -a
926	atf_check_equal "0" "$(zone_count reset)"
927}
928reset_wp_clears_fault_cleanup()
929{
930	gzoned_test_cleanup
931}
932
933atf_test_case delete_rejected cleanup
934delete_rejected_head()
935{
936	atf_set "descr" "gzoned rejects BIO_DELETE, hides TRIM support"
937	atf_set "require.user" "root"
938}
939delete_rejected_body()
940{
941	gzoned_test_setup
942
943	zoned_backing_md
944	atf_check gzoned create -s 256m ${md}
945	atf_check_equal "No" \
946	    "$(diskinfo -v /dev/${md}.zoned | \
947	    awk '/TRIM\/UNMAP support/ {print $1}')"
948	atf_check -s not-exit:0 -e ignore -o ignore \
949	    trim -f /dev/${md}.zoned
950}
951delete_rejected_cleanup()
952{
953	gzoned_test_cleanup
954}
955
956atf_test_case conventional_overlap cleanup
957conventional_overlap_head()
958{
959	atf_set "descr" "gzoned create -c refuses overlapping zone ranges"
960	atf_set "require.user" "root"
961}
962conventional_overlap_body()
963{
964	gzoned_test_setup
965
966	zoned_backing_md
967	atf_check -s not-exit:0 -e match:"overlaps" \
968	    gzoned create -s 256m -c 0-2,1-3 ${md}
969	atf_check test ! -c /dev/${md}.zoned
970	atf_check -s not-exit:0 -e match:"overlaps" \
971	    gzoned create -s 256m -c 1,1 ${md}
972	atf_check -s not-exit:0 -e match:"overlaps" \
973	    gzoned create -s 256m -c 0-3,2 ${md}
974	atf_check gzoned create -s 256m -c 0-1,2-3 ${md}
975	atf_check_equal "4" "$(zone_count nonwp)"
976}
977conventional_overlap_cleanup()
978{
979	gzoned_test_cleanup
980}
981
982atf_test_case resize cleanup
983resize_head()
984{
985	atf_set "descr" "Resizing the backing provider destroys the device"
986	atf_set "require.user" "root"
987}
988resize_body()
989{
990	gzoned_test_setup
991
992	zoned_backing_md
993	atf_check gzoned create -s 256m ${md}
994	atf_check test -c /dev/${md}.zoned
995
996	# The zone layout is fixed at creation, with the metadata sitting at the
997	# end of the provider: neither survives the provider changing size. The
998	# device is destroyed rather than left describing a disk that is no
999	# longer there.
1000	atf_check truncate -s 2049m backing_file
1001	atf_check mdconfig -r -u ${md} -s 2049m
1002	wait_dev_gone /dev/${md}.zoned
1003
1004	# Stale metadata is out of reach of the taste, at its new size: the
1005	# device does not come back on its own.
1006	true > /dev/${md}
1007	sleep 1
1008	atf_check test ! -c /dev/${md}.zoned
1009}
1010resize_cleanup()
1011{
1012	gzoned_test_cleanup
1013}
1014
1015atf_init_test_cases()
1016{
1017	atf_add_test_case create
1018	atf_add_test_case create_on_zoned
1019	atf_add_test_case is_zoned
1020	atf_add_test_case conventional
1021	atf_add_test_case conventional_single
1022	atf_add_test_case conventional_range
1023	atf_add_test_case conventional_ranges
1024	atf_add_test_case conventional_overlap
1025	atf_add_test_case all_zones_same
1026	atf_add_test_case zone_size
1027	atf_add_test_case zone_count_derived
1028	atf_add_test_case create_readonly
1029	atf_add_test_case zone_size_too_large
1030	atf_add_test_case zone_size_too_many
1031	atf_add_test_case reset_wp
1032	atf_add_test_case reset_wp_conv
1033	atf_add_test_case reset_wp_empty
1034	atf_add_test_case reset_wp_finished
1035	atf_add_test_case reset_wp_all_readonly
1036	atf_add_test_case zone_id_lowest_lba
1037	atf_add_test_case unrestricted_reads
1038	atf_add_test_case read_zone_types
1039	atf_add_test_case restricted_reads
1040	atf_add_test_case write_at_wp
1041	atf_add_test_case write_out_of_order
1042	atf_add_test_case write_boundary
1043	atf_add_test_case write_conv_boundary
1044	atf_add_test_case write_conv_anywhere
1045	atf_add_test_case write_full
1046	atf_add_test_case write_closed_zone
1047	atf_add_test_case persistence
1048	atf_add_test_case clear_clears_metadata
1049	atf_add_test_case max_open
1050	atf_add_test_case max_open_limit
1051	atf_add_test_case stop_force
1052	atf_add_test_case fault_injection
1053	atf_add_test_case reset_wp_clears_fault
1054	atf_add_test_case delete_rejected
1055	atf_add_test_case resize
1056}
1057