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