t/zbd: Combine write and read fio commands for test case #6
[fio.git] / t / zbd / test-zbd-support
1 #!/bin/bash
2 #
3 # Copyright (C) 2018 Western Digital Corporation or its affiliates.
4 #
5 # This file is released under the GPL.
6
7 usage() {
8     echo "Usage: $(basename "$0") [-d] [-e] [-l] [-r] [-v] [-t <test>] [-z] <SMR drive device node>"
9 }
10
11 max() {
12     if [ "$1" -gt "$2" ]; then
13         echo "$1"
14     else
15         echo "$2"
16     fi
17 }
18
19 min() {
20     if [ "$1" -lt "$2" ]; then
21         echo "$1"
22     else
23         echo "$2"
24     fi
25 }
26
27 ioengine() {
28         if [ -n "$use_libzbc" ]; then
29                 echo -n "--ioengine=libzbc"
30         else
31                 echo -n "--ioengine=$1"
32         fi
33 }
34
35 set_io_scheduler() {
36     local dev=$1 sched=$2
37
38     [ -e "/sys/block/$dev" ] || return $?
39     if [ -e "/sys/block/$dev/mq" ]; then
40         case "$sched" in
41             noop)        sched=none;;
42             deadline)    sched=mq-deadline;;
43         esac
44     else
45         case "$sched" in
46             none)        sched=noop;;
47             mq-deadline) sched=deadline;;
48         esac
49     fi
50
51     echo "$sched" >"/sys/block/$dev/queue/scheduler"
52 }
53
54 check_read() {
55     local read
56
57     read=$(fio_read <"${logfile}.${test_number}")
58     echo "read: $read <> $1" >> "${logfile}.${test_number}"
59     [ "$read" = "$1" ]
60 }
61
62 check_written() {
63     local written
64
65     written=$(fio_written <"${logfile}.${test_number}")
66     echo "written: $written <> $1" >> "${logfile}.${test_number}"
67     [ "$written" = "$1" ]
68 }
69
70 # Compare the reset count from the log file with reset count $2 using operator
71 # $1 (=, -ge, -gt, -le, -lt).
72 check_reset_count() {
73     local reset_count
74
75     reset_count=$(fio_reset_count <"${logfile}.${test_number}")
76     echo "reset_count: test $reset_count $1 $2" >> "${logfile}.${test_number}"
77     eval "[ '$reset_count' '$1' '$2' ]"
78 }
79
80 # Whether or not $1 (/dev/...) is a SCSI device.
81 is_scsi_device() {
82     local d f
83
84     d=$(basename "$dev")
85     for f in /sys/class/scsi_device/*/device/block/"$d"; do
86         [ -e "$f" ] && return 0
87     done
88     return 1
89 }
90
91 run_fio() {
92     local fio opts
93
94     fio=$(dirname "$0")/../../fio
95
96     opts=("--max-jobs=16" "--aux-path=/tmp" "--allow_file_create=0" \
97           "--significant_figures=10" "$@")
98     opts+=(${var_opts[@]})
99     { echo; echo "fio ${opts[*]}"; echo; } >>"${logfile}.${test_number}"
100
101     "${dynamic_analyzer[@]}" "$fio" "${opts[@]}"
102 }
103
104 run_one_fio_job() {
105     local r
106
107     r=$(((RANDOM << 16) | RANDOM))
108     run_fio --name="$dev" --filename="$dev" "$@" --randseed="$r"        \
109             --thread=1 --direct=1
110 }
111
112 write_and_run_one_fio_job() {
113     local r
114     local write_offset="${1}"
115     local write_size="${2}"
116
117     shift 2
118     r=$(((RANDOM << 16) | RANDOM))
119     run_fio --filename="$dev" --randseed="$r"  --name="write_job" --rw=write \
120             "$(ioengine "psync")" --bs="${logical_block_size}" \
121             --zonemode=zbd --zonesize="${zone_size}" --thread=1 --direct=1 \
122             --offset="${write_offset}" --size="${write_size}" \
123             --name="$dev" --wait_for="write_job" "$@" --thread=1 --direct=1
124 }
125
126 # Run fio on the first four sequential zones of the disk.
127 run_fio_on_seq() {
128     local opts=()
129
130     opts+=("--offset=$((first_sequential_zone_sector * 512))")
131     opts+=("--size=$((4 * zone_size))" "--zonemode=zbd")
132     if [ -z "$is_zbd" ]; then
133         opts+=("--zonesize=${zone_size}")
134     fi
135     run_one_fio_job "${opts[@]}" "$@"
136 }
137
138 # Check whether buffered writes are refused.
139 test1() {
140     run_fio --name=job1 --filename="$dev" --rw=write --direct=0 --bs=4K \
141             "$(ioengine "psync")" --size="${zone_size}" --thread=1      \
142             --zonemode=zbd --zonesize="${zone_size}" 2>&1 |
143         tee -a "${logfile}.${test_number}" |
144         grep -q 'Using direct I/O is mandatory for writing to ZBD drives'
145     local fio_rc=${PIPESTATUS[0]} grep_rc=${PIPESTATUS[2]}
146     case "$fio_rc" in
147         0|1) ;;
148         *)   return "$fio_rc"
149     esac
150     if [ -n "$is_zbd" ]; then
151         [ "$grep_rc" = 0 ]
152     else
153         [ "$grep_rc" != 0 ]
154     fi
155 }
156
157 # Block size exceeds zone size.
158 test2() {
159     local bs off opts=() rc
160
161     off=$(((first_sequential_zone_sector + 2 * sectors_per_zone) * 512))
162     bs=$((2 * zone_size))
163     opts+=("$(ioengine "psync")")
164     opts+=("--name=job1" "--filename=$dev" "--rw=write" "--direct=1")
165     opts+=("--zonemode=zbd" "--offset=$off" "--bs=$bs" "--size=$bs")
166     if [ -z "$is_zbd" ]; then
167         opts+=("--zonesize=${zone_size}")
168     fi
169     run_fio "${opts[@]}" >> "${logfile}.${test_number}" 2>&1 || return $?
170     ! grep -q 'WRITE:' "${logfile}.${test_number}"
171 }
172
173 # Run fio against an empty zone. This causes fio to report "No I/O performed".
174 test3() {
175     local off opts=() rc
176
177     off=$((first_sequential_zone_sector * 512 + 128 * zone_size))
178     size=$((zone_size))
179     [ -n "$is_zbd" ] && reset_zone "$dev" $((off / 512))
180     opts+=("--name=$dev" "--filename=$dev" "--offset=$off" "--bs=4K")
181     opts+=("--size=$size" "--zonemode=zbd")
182     opts+=("$(ioengine "psync")" "--rw=read" "--direct=1" "--thread=1")
183     if [ -z "$is_zbd" ]; then
184         opts+=("--zonesize=${zone_size}")
185     fi
186     run_fio "${opts[@]}" >> "${logfile}.${test_number}" 2>&1 || return $?
187     ! grep -q 'READ:' "${logfile}.${test_number}"
188 }
189
190 # Run fio with --read_beyond_wp=1 against an empty zone.
191 test4() {
192     local off opts=()
193
194     off=$((first_sequential_zone_sector * 512 + 129 * zone_size))
195     size=$((zone_size))
196     [ -n "$is_zbd" ] && reset_zone "$dev" $((off / 512))
197     opts+=("--name=$dev" "--filename=$dev" "--offset=$off" "--bs=$size")
198     opts+=("--size=$size" "--thread=1" "--read_beyond_wp=1")
199     opts+=("$(ioengine "psync")" "--rw=read" "--direct=1" "--disable_lat=1")
200     opts+=("--zonemode=zbd" "--zonesize=${zone_size}")
201     run_fio "${opts[@]}" >> "${logfile}.${test_number}" 2>&1 || return $?
202     check_read $size || return $?
203 }
204
205 # Sequential write to sequential zones.
206 test5() {
207     local size
208
209     size=$((4 * zone_size))
210     run_fio_on_seq "$(ioengine "psync")" --iodepth=1 --rw=write \
211                    --bs="$(max $((zone_size / 64)) "$logical_block_size")"\
212                    --do_verify=1 --verify=md5                           \
213                    >>"${logfile}.${test_number}" 2>&1 || return $?
214     check_written $size || return $?
215     check_read $size || return $?
216 }
217
218 # Sequential read from sequential zones.
219 test6() {
220     local size
221
222     size=$((4 * zone_size))
223     write_and_run_one_fio_job \
224             $((first_sequential_zone_sector * 512)) "${size}" \
225             --offset=$((first_sequential_zone_sector * 512)) \
226             --size="${size}" --zonemode=zbd --zonesize="${zone_size}" \
227             "$(ioengine "psync")" --iodepth=1 --rw=read \
228             --bs="$(max $((zone_size / 64)) "$logical_block_size")" \
229             >>"${logfile}.${test_number}" 2>&1 || return $?
230     check_read $size || return $?
231 }
232
233 # Random write to sequential zones, libaio, queue depth 1.
234 test7() {
235     local size=$((zone_size))
236
237     run_fio_on_seq "$(ioengine "libaio")" --iodepth=1 --rw=randwrite    \
238                    --bs="$(min 16384 "${zone_size}")"                   \
239                    --do_verify=1 --verify=md5 --size="$size"            \
240                    >>"${logfile}.${test_number}" 2>&1 || return $?
241     check_written $size || return $?
242     check_read $size || return $?
243 }
244
245 # Random write to sequential zones, libaio, queue depth 64.
246 test8() {
247     local size
248
249     size=$((4 * zone_size))
250     run_fio_on_seq "$(ioengine "libaio")" --iodepth=64 --rw=randwrite   \
251                    --bs="$(min 16384 "${zone_size}")"                   \
252                    --do_verify=1 --verify=md5                           \
253                    >>"${logfile}.${test_number}" 2>&1 || return $?
254     check_written $size || return $?
255     check_read $size || return $?
256 }
257
258 # Random write to sequential zones, sg, queue depth 1.
259 test9() {
260     local size
261
262     if ! is_scsi_device "$dev"; then
263         echo "$dev is not a SCSI device" >>"${logfile}.${test_number}"
264         return 0
265     fi
266
267     size=$((4 * zone_size))
268     run_fio_on_seq --ioengine=sg                                        \
269                    --iodepth=1 --rw=randwrite --bs=16K                  \
270                    --do_verify=1 --verify=md5                           \
271                    >>"${logfile}.${test_number}" 2>&1 || return $?
272     check_written $size || return $?
273     check_read $size || return $?
274 }
275
276 # Random write to sequential zones, sg, queue depth 64.
277 test10() {
278     local size
279
280     if ! is_scsi_device "$dev"; then
281         echo "$dev is not a SCSI device" >>"${logfile}.${test_number}"
282         return 0
283     fi
284
285     size=$((4 * zone_size))
286     run_fio_on_seq --ioengine=sg                                        \
287                    --iodepth=64 --rw=randwrite --bs=16K                 \
288                    --do_verify=1 --verify=md5                           \
289                    >>"${logfile}.${test_number}" 2>&1 || return $?
290     check_written $size || return $?
291     check_read $size || return $?
292 }
293
294 # Random write to sequential zones, libaio, queue depth 64, random block size.
295 test11() {
296     local size
297
298     size=$((4 * zone_size))
299     run_fio_on_seq "$(ioengine "libaio")" --iodepth=64 --rw=randwrite   \
300                    --bsrange=4K-64K --do_verify=1 --verify=md5          \
301                    --debug=zbd >>"${logfile}.${test_number}" 2>&1 || return $?
302     check_written $size || return $?
303     check_read $size || return $?
304 }
305
306 # Random write to sequential zones, libaio, queue depth 64, max 1 open zone.
307 test12() {
308     local size
309
310     size=$((8 * zone_size))
311     run_fio_on_seq "$(ioengine "libaio")" --iodepth=64 --rw=randwrite --bs=16K \
312                    --max_open_zones=1 --size=$size --do_verify=1 --verify=md5 \
313                    --debug=zbd >>"${logfile}.${test_number}" 2>&1 || return $?
314     check_written $size || return $?
315     check_read $size || return $?
316 }
317
318 # Random write to sequential zones, libaio, queue depth 64, max 4 open zones.
319 test13() {
320     local size
321
322     size=$((8 * zone_size))
323     run_fio_on_seq "$(ioengine "libaio")" --iodepth=64 --rw=randwrite --bs=16K \
324                    --max_open_zones=4 --size=$size --do_verify=1 --verify=md5 \
325                    --debug=zbd                                                \
326                    >>"${logfile}.${test_number}" 2>&1 || return $?
327     check_written $size || return $?
328     check_read $size || return $?
329 }
330
331 # Random write to conventional zones.
332 test14() {
333     local size
334
335     size=$((16 * 2**20)) # 20 MB
336     if [ $size -gt $((first_sequential_zone_sector * 512)) ]; then
337         echo "$dev does not have enough sequential zones" \
338              >>"${logfile}.${test_number}"
339         return 0
340     fi
341     run_one_fio_job "$(ioengine "libaio")" --iodepth=64 --rw=randwrite --bs=16K \
342                     --zonemode=zbd --zonesize="${zone_size}" --do_verify=1 \
343                     --verify=md5 --size=$size                              \
344                     >>"${logfile}.${test_number}" 2>&1 || return $?
345     check_written $((size)) || return $?
346     check_read $((size)) || return $?
347 }
348
349 # Sequential read on a mix of empty and full zones.
350 test15() {
351     local i off size
352
353     for ((i=0;i<4;i++)); do
354         [ -n "$is_zbd" ] &&
355             reset_zone "$dev" $((first_sequential_zone_sector +
356                                  i*sectors_per_zone))
357     done
358     off=$(((first_sequential_zone_sector + 2 * sectors_per_zone) * 512))
359     size=$((2 * zone_size))
360     run_one_fio_job "$(ioengine "psync")" --rw=write --bs=$((zone_size / 16))\
361                     --zonemode=zbd --zonesize="${zone_size}" --offset=$off \
362                     --size=$size >>"${logfile}.${test_number}" 2>&1 ||
363         return $?
364     check_written $size || return $?
365     off=$((first_sequential_zone_sector * 512))
366     size=$((4 * zone_size))
367     run_one_fio_job "$(ioengine "psync")" --rw=read --bs=$((zone_size / 16)) \
368                     --zonemode=zbd --zonesize="${zone_size}" --offset=$off \
369                     --size=$((size)) >>"${logfile}.${test_number}" 2>&1 ||
370         return $?
371     if [ -n "$is_zbd" ]; then
372         check_read $((size / 2))
373     else
374         check_read $size
375     fi
376 }
377
378 # Random read on a mix of empty and full zones. Must be run after test15.
379 test16() {
380     local off size
381
382     off=$((first_sequential_zone_sector * 512))
383     size=$((4 * zone_size))
384     run_one_fio_job "$(ioengine "libaio")" --iodepth=64 --rw=randread --bs=16K \
385                     --zonemode=zbd --zonesize="${zone_size}" --offset=$off \
386                     --size=$size >>"${logfile}.${test_number}" 2>&1 || return $?
387     check_read $size || return $?
388 }
389
390 # Random reads and writes in the last zone.
391 test17() {
392     local io off read size written
393
394     off=$(((disk_size / zone_size - 1) * zone_size))
395     size=$((disk_size - off))
396     # Overwrite the last zone to avoid that reading from that zone fails.
397     if [ -n "$is_zbd" ]; then
398         reset_zone "$dev" $((off / 512)) || return $?
399     fi
400     run_one_fio_job "$(ioengine "psync")" --rw=write --offset="$off"    \
401                     --zonemode=zbd --zonesize="${zone_size}"            \
402                     --bs="$zone_size" --size="$zone_size"               \
403                     >>"${logfile}.${test_number}" 2>&1 || return $?
404     check_written "$zone_size" || return $?
405     run_one_fio_job "$(ioengine "libaio")" --iodepth=8 --rw=randrw --bs=4K \
406                     --zonemode=zbd --zonesize="${zone_size}"            \
407                     --offset=$off --loops=2 --norandommap=1\
408                     >>"${logfile}.${test_number}" 2>&1 || return $?
409     written=$(fio_written <"${logfile}.${test_number}")
410     read=$(fio_read <"${logfile}.${test_number}")
411     io=$((written + read))
412     echo "Total number of bytes read and written: $io <> $size" \
413          >>"${logfile}.${test_number}"
414     [ $io = $((size * 2)) ];
415 }
416
417 # Out-of-range zone reset threshold and frequency parameters.
418 test18() {
419     run_fio_on_seq --zone_reset_threshold=-1 |&
420         tee -a "${logfile}.${test_number}"   |
421             grep -q 'value out of range' || return $?
422 }
423
424 test19() {
425     run_fio_on_seq --zone_reset_threshold=2  |&
426         tee -a "${logfile}.${test_number}"   |
427         grep -q 'value out of range' || return $?
428 }
429
430 test20() {
431     run_fio_on_seq --zone_reset_threshold=.4:.6 |&
432         tee -a "${logfile}.${test_number}"   |
433         grep -q 'the list exceeding max length' || return $?
434 }
435
436 test21() {
437     run_fio_on_seq --zone_reset_frequency=-1 |&
438         tee -a "${logfile}.${test_number}"   |
439         grep -q 'value out of range' || return $?
440 }
441
442 test22() {
443     run_fio_on_seq --zone_reset_frequency=2  |&
444         tee -a "${logfile}.${test_number}"   |
445         grep -q 'value out of range' || return $?
446 }
447
448 test23() {
449     run_fio_on_seq --zone_reset_frequency=.4:.6  |&
450         tee -a "${logfile}.${test_number}"   |
451         grep -q 'the list exceeding max length' || return $?
452 }
453
454 test24() {
455     local bs loops=9 size=$((zone_size))
456
457     bs=$(min $((256*1024)) "$zone_size")
458     run_fio_on_seq "$(ioengine "psync")" --rw=write --bs="$bs"          \
459                    --size=$size --loops=$loops                          \
460                    --zone_reset_frequency=.01 --zone_reset_threshold=.90 \
461                    >> "${logfile}.${test_number}" 2>&1 || return $?
462     check_written $((size * loops)) || return $?
463     check_reset_count -eq 8 ||
464         check_reset_count -eq 9 ||
465         check_reset_count -eq 10 || return $?
466 }
467
468 # Multiple non-overlapping sequential write jobs for the same drive.
469 test25() {
470     local i opts=()
471
472     for ((i=0;i<16;i++)); do
473         [ -n "$is_zbd" ] &&
474             reset_zone "$dev" $((first_sequential_zone_sector + i*sectors_per_zone))
475     done
476     for ((i=0;i<16;i++)); do
477         opts+=("--name=job$i" "--filename=$dev" "--thread=1" "--direct=1")
478         opts+=("--offset=$((first_sequential_zone_sector*512 + zone_size*i))")
479         opts+=("--size=$zone_size" "$(ioengine "psync")" "--rw=write" "--bs=16K")
480         opts+=("--zonemode=zbd" "--zonesize=${zone_size}" "--group_reporting=1")
481         opts+=(${var_opts[@]})
482     done
483     run_fio "${opts[@]}" >> "${logfile}.${test_number}" 2>&1 || return $?
484 }
485
486 write_to_first_seq_zone() {
487     local loops=4 r
488
489     r=$(((RANDOM << 16) | RANDOM))
490     run_fio --name="$dev" --filename="$dev" "$(ioengine "psync")" --rw="$1" \
491             --thread=1 --do_verify=1 --verify=md5 --direct=1 --bs=4K    \
492             --offset=$((first_sequential_zone_sector * 512))            \
493             "--size=$zone_size" --loops=$loops --randseed="$r"          \
494             --zonemode=zbd --zonesize="${zone_size}" --group_reporting=1        \
495             --gtod_reduce=1 >> "${logfile}.${test_number}" 2>&1 || return $?
496     check_written $((loops * zone_size)) || return $?
497 }
498
499 # Overwrite the first sequential zone four times sequentially.
500 test26() {
501     write_to_first_seq_zone write
502 }
503
504 # Overwrite the first sequential zone four times using random writes.
505 test27() {
506     write_to_first_seq_zone randwrite
507 }
508
509 # Multiple overlapping random write jobs for the same drive.
510 test28() {
511     local i jobs=16 off opts
512
513     off=$((first_sequential_zone_sector * 512 + 64 * zone_size))
514     [ -n "$is_zbd" ] && reset_zone "$dev" $((off / 512))
515     opts=("--debug=zbd")
516     for ((i=0;i<jobs;i++)); do
517         opts+=("--name=job$i" "--filename=$dev" "--offset=$off" "--bs=16K")
518         opts+=("--size=$zone_size" "$(ioengine "psync")" "--rw=randwrite")
519         opts+=("--thread=1" "--direct=1" "--zonemode=zbd")
520         opts+=("--zonesize=${zone_size}" "--group_reporting=1")
521         opts+=(${var_opts[@]})
522     done
523     run_fio "${opts[@]}" >> "${logfile}.${test_number}" 2>&1 || return $?
524     check_written $((jobs * zone_size)) || return $?
525     check_reset_count -eq $jobs ||
526         check_reset_count -eq $((jobs - 1)) ||
527         return $?
528 }
529
530 # Multiple overlapping random write jobs for the same drive and with a limited
531 # number of open zones.
532 test29() {
533     local i jobs=16 off opts=()
534
535     off=$((first_sequential_zone_sector * 512 + 64 * zone_size))
536     size=$((16*zone_size))
537     [ -n "$is_zbd" ] && reset_zone "$dev" $((off / 512))
538     opts=("--debug=zbd")
539     for ((i=0;i<jobs;i++)); do
540         opts+=("--name=job$i" "--filename=$dev" "--offset=$off" "--bs=16K")
541         opts+=("--size=$size" "--io_size=$zone_size" "--thread=1")
542         opts+=("$(ioengine "psync")" "--rw=randwrite" "--direct=1")
543         opts+=("--max_open_zones=4" "--group_reporting=1")
544         opts+=("--zonemode=zbd" "--zonesize=${zone_size}")
545         opts+=(${var_opts[@]})
546     done
547     run_fio "${opts[@]}" >> "${logfile}.${test_number}" 2>&1 || return $?
548     check_written $((jobs * zone_size)) || return $?
549 }
550
551 # Random reads and writes across the entire disk for 30s.
552 test30() {
553     local off
554
555     off=$((first_sequential_zone_sector * 512))
556     run_one_fio_job "$(ioengine "libaio")" --iodepth=8 --rw=randrw      \
557                     --bs="$(max $((zone_size / 128)) "$logical_block_size")"\
558                     --zonemode=zbd --zonesize="${zone_size}" --offset=$off\
559                     --loops=2 --time_based --runtime=30s --norandommap=1\
560                     >>"${logfile}.${test_number}" 2>&1
561 }
562
563 # Random reads across all sequential zones for 30s. This is not only a fio
564 # test but also allows to verify the performance of a drive.
565 test31() {
566     local bs inc nz off opts size
567
568     # Start with writing 128 KB to 128 sequential zones.
569     bs=128K
570     nz=128
571     # shellcheck disable=SC2017
572     inc=$(((disk_size - (first_sequential_zone_sector * 512)) / (nz * zone_size)
573            * zone_size))
574     opts=()
575     for ((off = first_sequential_zone_sector * 512; off < disk_size;
576           off += inc)); do
577         opts+=("--name=$dev" "--filename=$dev" "--offset=$off" "--io_size=$bs")
578         opts+=("--bs=$bs" "--size=$zone_size" "$(ioengine "libaio")")
579         opts+=("--rw=write" "--direct=1" "--thread=1" "--stats=0")
580         opts+=("--zonemode=zbd" "--zonesize=${zone_size}")
581         opts+=(${var_opts[@]})
582     done
583     "$(dirname "$0")/../../fio" "${opts[@]}" >> "${logfile}.${test_number}" 2>&1
584     # Next, run the test.
585     off=$((first_sequential_zone_sector * 512))
586     size=$((disk_size - off))
587     opts=("--name=$dev" "--filename=$dev" "--offset=$off" "--size=$size")
588     opts+=("--bs=$bs" "$(ioengine "psync")" "--rw=randread" "--direct=1")
589     opts+=("--thread=1" "--time_based" "--runtime=30" "--zonemode=zbd")
590     opts+=("--zonesize=${zone_size}")
591     run_fio "${opts[@]}" >> "${logfile}.${test_number}" 2>&1 || return $?
592 }
593
594 # Random writes across all sequential zones. This is not only a fio test but
595 # also allows to verify the performance of a drive.
596 test32() {
597     local off opts=() size
598
599     off=$((first_sequential_zone_sector * 512))
600     size=$((disk_size - off))
601     opts+=("--name=$dev" "--filename=$dev" "--offset=$off" "--size=$size")
602     opts+=("--bs=128K" "$(ioengine "psync")" "--rw=randwrite" "--direct=1")
603     opts+=("--thread=1" "--time_based" "--runtime=30")
604     opts+=("--max_open_zones=$max_open_zones" "--zonemode=zbd")
605     opts+=("--zonesize=${zone_size}")
606     run_fio "${opts[@]}" >> "${logfile}.${test_number}" 2>&1 || return $?
607 }
608
609 # Write to sequential zones with a block size that is not a divisor of the
610 # zone size.
611 test33() {
612     local bs io_size size
613
614     size=$((2 * zone_size))
615     io_size=$((5 * zone_size))
616     bs=$((3 * zone_size / 4))
617     run_fio_on_seq "$(ioengine "psync")" --iodepth=1 --rw=write \
618                    --size=$size --io_size=$io_size --bs=$bs     \
619                    >> "${logfile}.${test_number}" 2>&1 || return $?
620     check_written $(((io_size + bs - 1) / bs * bs)) || return $?
621 }
622
623 # Write to sequential zones with a block size that is not a divisor of the
624 # zone size and with data verification enabled.
625 test34() {
626     local size
627
628     size=$((2 * zone_size))
629     run_fio_on_seq "$(ioengine "psync")" --iodepth=1 --rw=write --size=$size \
630                    --do_verify=1 --verify=md5 --bs=$((3 * zone_size / 4)) \
631                    >> "${logfile}.${test_number}" 2>&1 && return 1
632     grep -q 'not a divisor of' "${logfile}.${test_number}"
633 }
634
635 # Test 1/4 for the I/O boundary rounding code: $size < $zone_size.
636 test35() {
637     local bs off io_size size
638
639     off=$(((first_sequential_zone_sector + 1) * 512))
640     size=$((zone_size - 2 * 512))
641     bs=$((zone_size / 4))
642     run_one_fio_job --offset=$off --size=$size "$(ioengine "psync")"    \
643                     --iodepth=1 --rw=write --do_verify=1 --verify=md5   \
644                     --bs=$bs --zonemode=zbd --zonesize="${zone_size}"   \
645                     >> "${logfile}.${test_number}" 2>&1 && return 1
646     grep -q 'io_size must be at least one zone' "${logfile}.${test_number}"
647 }
648
649 # Test 2/4 for the I/O boundary rounding code: $size < $zone_size.
650 test36() {
651     local bs off io_size size
652
653     off=$(((first_sequential_zone_sector) * 512))
654     size=$((zone_size - 512))
655     bs=$((zone_size / 4))
656     run_one_fio_job --offset=$off --size=$size "$(ioengine "psync")"    \
657                     --iodepth=1 --rw=write --do_verify=1 --verify=md5   \
658                     --bs=$bs --zonemode=zbd --zonesize="${zone_size}"   \
659                     >> "${logfile}.${test_number}" 2>&1 && return 1
660     grep -q 'io_size must be at least one zone' "${logfile}.${test_number}"
661 }
662
663 # Test 3/4 for the I/O boundary rounding code: $size > $zone_size.
664 test37() {
665     local bs off size
666
667     if [ "$first_sequential_zone_sector" = 0 ]; then
668         off=0
669     else
670         off=$(((first_sequential_zone_sector - 1) * 512))
671     fi
672     size=$((zone_size + 2 * 512))
673     bs=$((zone_size / 4))
674     run_one_fio_job --offset=$off --size=$size "$(ioengine "psync")"    \
675                     --iodepth=1 --rw=write --do_verify=1 --verify=md5   \
676                     --bs=$bs --zonemode=zbd --zonesize="${zone_size}"   \
677                     >> "${logfile}.${test_number}" 2>&1
678     check_written $((zone_size)) || return $?
679 }
680
681 # Test 4/4 for the I/O boundary rounding code: $offset > $disk_size - $zone_size
682 test38() {
683     local bs off size
684
685     size=$((logical_block_size))
686     off=$((disk_size - logical_block_size))
687     bs=$((logical_block_size))
688     run_one_fio_job --offset=$off --size=$size "$(ioengine "psync")"    \
689                     --iodepth=1 --rw=write --do_verify=1 --verify=md5   \
690                     --bs=$bs --zonemode=zbd --zonesize="${zone_size}"   \
691                     >> "${logfile}.${test_number}" 2>&1 && return 1
692     grep -q 'io_size must be at least one zone' "${logfile}.${test_number}"
693 }
694
695 # Read one block from a block device.
696 read_one_block() {
697     local bs
698
699     bs=$((logical_block_size))
700     run_one_fio_job --rw=read "$(ioengine "psync")" --bs=$bs --size=$bs "$@" 2>&1 |
701         tee -a "${logfile}.${test_number}"
702 }
703
704 # Check whether fio accepts --zonemode=none for zoned block devices.
705 test39() {
706     [ -n "$is_zbd" ] || return 0
707     read_one_block --zonemode=none >/dev/null || return $?
708     check_read $((logical_block_size)) || return $?
709 }
710
711 # Check whether fio accepts --zonemode=strided for zoned block devices.
712 test40() {
713     local bs
714
715     bs=$((logical_block_size))
716     [ -n "$is_zbd" ] || return 0
717     read_one_block --zonemode=strided |
718         grep -q 'fio: --zonesize must be specified when using --zonemode=strided' ||
719         return $?
720     read_one_block --zonemode=strided --zonesize=$bs >/dev/null || return $?
721     check_read $bs || return $?
722 }
723
724 # Check whether fio checks the zone size for zoned block devices.
725 test41() {
726     [ -n "$is_zbd" ] || return 0
727     read_one_block --zonemode=zbd --zonesize=$((2 * zone_size)) |
728         grep -q 'job parameter zonesize.*does not match disk zone size'
729 }
730
731 # Check whether fio handles --zonesize=0 correctly for regular block devices.
732 test42() {
733     [ -n "$is_zbd" ] && return 0
734     read_one_block --zonemode=zbd --zonesize=0 |
735         grep -q 'Specifying the zone size is mandatory for regular block devices with --zonemode=zbd'
736 }
737
738 # Check whether fio handles --zonesize=1 correctly for regular block devices.
739 test43() {
740     [ -n "$is_zbd" ] && return 0
741     read_one_block --zonemode=zbd --zonesize=1 |
742         grep -q 'zone size must be at least 512 bytes for --zonemode=zbd'
743 }
744
745 # Check whether fio handles --zonemode=none --zonesize=1 correctly.
746 test44() {
747     read_one_block --zonemode=none --zonesize=1 |
748         grep -q 'fio: --zonemode=none and --zonesize are not compatible'
749 }
750
751 test45() {
752     local bs i
753
754     [ -z "$is_zbd" ] && return 0
755     bs=$((logical_block_size))
756     run_one_fio_job "$(ioengine "psync")" --iodepth=1 --rw=randwrite --bs=$bs\
757                     --offset=$((first_sequential_zone_sector * 512)) \
758                     --size="$zone_size" --do_verify=1 --verify=md5 2>&1 |
759         tee -a "${logfile}.${test_number}" |
760         grep -q "fio: first I/O failed. If .* is a zoned block device, consider --zonemode=zbd"
761 }
762
763 # Random write to sequential zones, libaio, 8 jobs, queue depth 64 per job
764 test46() {
765     local size
766
767     size=$((4 * zone_size))
768     run_fio_on_seq "$(ioengine "libaio")" --iodepth=64 --rw=randwrite --bs=4K \
769                    --group_reporting=1 --numjobs=8 \
770                    >> "${logfile}.${test_number}" 2>&1 || return $?
771     check_written $((size * 8)) || return $?
772 }
773
774 # Check whether fio handles --zonemode=zbd --zoneskip=1 correctly.
775 test47() {
776     local bs
777
778     [ -z "$is_zbd" ] && return 0
779     bs=$((logical_block_size))
780     run_one_fio_job "$(ioengine "psync")" --rw=write --bs=$bs \
781                     --zonemode=zbd --zoneskip=1          \
782                     >> "${logfile}.${test_number}" 2>&1 && return 1
783     grep -q 'zoneskip 1 is not a multiple of the device zone size' "${logfile}.${test_number}"
784 }
785
786 # Multiple overlapping random write jobs for the same drive and with a
787 # limited number of open zones. This is similar to test29, but uses libaio
788 # to stress test zone locking.
789 test48() {
790     local i jobs=16 off opts=()
791
792     off=$((first_sequential_zone_sector * 512 + 64 * zone_size))
793     size=$((16*zone_size))
794     [ -n "$is_zbd" ] && reset_zone "$dev" $((off / 512))
795     opts=("--aux-path=/tmp" "--allow_file_create=0" "--significant_figures=10")
796     opts+=("--debug=zbd")
797     opts+=("$(ioengine "libaio")" "--rw=randwrite" "--direct=1")
798     opts+=("--time_based" "--runtime=30")
799     opts+=("--zonemode=zbd" "--zonesize=${zone_size}")
800     opts+=("--max_open_zones=4")
801     for ((i=0;i<jobs;i++)); do
802         opts+=("--name=job$i" "--filename=$dev" "--offset=$off" "--bs=16K")
803         opts+=("--io_size=$zone_size" "--iodepth=256" "--thread=1")
804         opts+=("--group_reporting=1")
805     done
806
807     fio=$(dirname "$0")/../../fio
808
809     { echo; echo "fio ${opts[*]}"; echo; } >>"${logfile}.${test_number}"
810
811     timeout -v -s KILL 45s \
812             "${dynamic_analyzer[@]}" "$fio" "${opts[@]}" \
813             >> "${logfile}.${test_number}" 2>&1 || return $?
814 }
815
816 tests=()
817 dynamic_analyzer=()
818 reset_all_zones=
819 use_libzbc=
820 zbd_debug=
821
822 while [ "${1#-}" != "$1" ]; do
823   case "$1" in
824     -d) dynamic_analyzer=(valgrind "--read-var-info=yes" "--tool=drd"
825                           "--show-confl-seg=no");
826         shift;;
827     -e) dynamic_analyzer=(valgrind "--read-var-info=yes" "--tool=helgrind");
828         shift;;
829     -l) use_libzbc=1; shift;;
830     -r) reset_all_zones=1; shift;;
831     -t) tests+=("$2"); shift; shift;;
832     -v) dynamic_analyzer=(valgrind "--read-var-info=yes");
833         shift;;
834     -z) zbd_debug=1; shift;;
835     --) shift; break;;
836   esac
837 done
838
839 if [ $# != 1 ]; then
840     usage
841     exit 1
842 fi
843
844 # shellcheck source=functions
845 source "$(dirname "$0")/functions" || exit $?
846
847 var_opts=()
848 if [ -n "$zbd_debug" ]; then
849     var_opts+=("--debug=zbd")
850 fi
851 dev=$1
852 realdev=$(readlink -f "$dev")
853 basename=$(basename "$realdev")
854
855 if [[ -b "$realdev" ]]; then
856         major=$((0x$(stat -L -c '%t' "$realdev"))) || exit $?
857         minor=$((0x$(stat -L -c '%T' "$realdev"))) || exit $?
858         disk_size=$(($(<"/sys/dev/block/$major:$minor/size")*512))
859
860         # When the target is a partition device, get basename of its
861         # holder device to access sysfs path of the holder device
862         if [[ -r "/sys/dev/block/$major:$minor/partition" ]]; then
863                 realsysfs=$(readlink "/sys/dev/block/$major:$minor")
864                 basename=$(basename "${realsysfs%/*}")
865         fi
866         logical_block_size=$(<"/sys/block/$basename/queue/logical_block_size")
867         case "$(<"/sys/class/block/$basename/queue/zoned")" in
868         host-managed|host-aware)
869                 is_zbd=true
870                 if ! result=($(first_sequential_zone "$dev")); then
871                         echo "Failed to determine first sequential zone"
872                         exit 1
873                 fi
874                 first_sequential_zone_sector=${result[0]}
875                 sectors_per_zone=${result[1]}
876                 zone_size=$((sectors_per_zone * 512))
877                 if ! max_open_zones=$(max_open_zones "$dev"); then
878                         echo "Failed to determine maximum number of open zones"
879                         exit 1
880                 fi
881                 set_io_scheduler "$basename" deadline || exit $?
882                 if [ -n "$reset_all_zones" ]; then
883                         reset_zone "$dev" -1
884                 fi
885                 ;;
886         *)
887                 first_sequential_zone_sector=$(((disk_size / 2) &
888                                                 (logical_block_size - 1)))
889                 zone_size=$(max 65536 "$logical_block_size")
890                 sectors_per_zone=$((zone_size / 512))
891                 max_open_zones=128
892                 set_io_scheduler "$basename" none || exit $?
893                 ;;
894         esac
895 elif [[ -c "$realdev" ]]; then
896         # For an SG node, we must have libzbc option specified
897         if [[ ! -n "$use_libzbc" ]]; then
898                 echo "Character device files can only be used with -l (libzbc) option"
899                 exit 1
900         fi
901
902         if ! $(is_zbc "$dev"); then
903                 echo "Device is not a ZBC disk"
904                 exit 1
905         fi
906         is_zbd=true
907
908         if ! disk_size=($(( $(zbc_disk_sectors "$dev") * 512))); then
909                 echo "Failed to determine disk size"
910                 exit 1
911         fi
912         if ! logical_block_size=($(zbc_logical_block_size "$dev")); then
913                 echo "Failed to determine logical block size"
914                 exit 1
915         fi
916         if ! result=($(first_sequential_zone "$dev")); then
917                 echo "Failed to determine first sequential zone"
918                 exit 1
919         fi
920         first_sequential_zone_sector=${result[0]}
921         sectors_per_zone=${result[1]}
922         zone_size=$((sectors_per_zone * 512))
923         if ! max_open_zones=$(max_open_zones "$dev"); then
924                 echo "Failed to determine maximum number of open zones"
925                 exit 1
926         fi
927         if [ -n "$reset_all_zones" ]; then
928                 reset_zone "$dev" -1
929         fi
930 fi
931
932 echo -n "First sequential zone starts at sector $first_sequential_zone_sector;"
933 echo " zone size: $((zone_size >> 20)) MB"
934
935 if [ "${#tests[@]}" = 0 ]; then
936     readarray -t tests < <(declare -F | grep "test[0-9]*" | \
937                                    tr -c -d "[:digit:]\n" | sort -n)
938 fi
939
940 logfile=$0.log
941
942 passed=0
943 failed=0
944 if [ -t 1 ]; then
945     red="\e[1;31m"
946     green="\e[1;32m"
947     end="\e[m"
948 else
949     red=""
950     green=""
951     end=""
952 fi
953 rc=0
954
955 intr=0
956 trap 'intr=1' SIGINT
957
958 for test_number in "${tests[@]}"; do
959     rm -f "${logfile}.${test_number}"
960     echo -n "Running test $(printf "%02d" $test_number) ... "
961     if eval "test$test_number"; then
962         status="PASS"
963         cc_status="${green}${status}${end}"
964         ((passed++))
965     else
966         status="FAIL"
967         cc_status="${red}${status}${end}"
968         ((failed++))
969         rc=1
970     fi
971     echo -e "$cc_status"
972     echo "$status" >> "${logfile}.${test_number}"
973     [ $intr -ne 0 ] && exit 1
974 done
975
976 echo "$passed tests passed"
977 if [ $failed -gt 0 ]; then
978     echo " and $failed tests failed"
979 fi
980 exit $rc