Whamcloud - gitweb
- add an overwrite pass
[fs/lustre-release.git] / lustre / scripts / bdev-io-survey.sh
1 #!/bin/bash
2
3 # for now all the units are in 'k', but we could introduce some helpers
4 # would be nice to run tests in the background and trap signals and kill
5 #
6 #  todo:
7 #       make sure devices aren't in use before going to town
8 #       really use threads with iozone
9 #       look into what sgp_dd is really doing, update arguments
10 #       rename config/prepare/setup/cleanup/finish/teardown
11 #       do something with sf and fpp iterating
12 #       discard first vmstat line
13 #
14
15 # a temp dir that is setup and torn down for each script run
16 tmpdir=""
17 # so we can kill background processes as the test cleans up
18 declare -a cleanup_pids
19 # to unmount mounts in our tmpdir before removing it
20 declare -a cleanup_mounts
21 # global for completing the table.  XXX this is a wart that could go
22 cur_y="0"
23 # a global which funcs use to get at the blocks[] array
24 last_block=-1
25
26 # defaults for some options:
27 min_threads=1
28 max_threads=4
29 possible_tests="sgp_dd ext2_iozone echo_filter"
30 run_tests="$possible_tests"
31 echo_module=""
32
33 # optional output directory
34 output_dir=""
35  
36 die() {
37         echo $* 1>&2
38         exit 1
39 }
40 rm_or_die() {
41         for path in $*; do
42                 [ -e $path ] || continue;
43                 [ -f $path ] || die "needed to remove non-file $path"
44                 rm -f $path || die "couldn't remove $path"
45         done
46 }
47 save_output() {
48         [ ! -z "$output_dir" ] && mv -f $1 $output_dir/$2
49 }
50 cleanup() {
51         # only cleanup test runs if we have block devices
52         if [ $last_block != -1 ]; then
53                 for pid in ${cleanup_pids[*]}; do
54                         kill $pid
55                 done
56                 cleanup_echo_filter
57                 for a in ${cleanup_mounts[*]}; do
58                         umount -f $a
59                 done
60         fi
61
62         [ ${#tmpdir} == 18 ] && [ -d $tmpdir ] && rm -rf $tmpdir
63 }
64 trap cleanup EXIT
65
66 pid_now_running() {
67         local pid=$1
68         cleanup_pids[$pid]=$pid
69 }
70 pid_has_stopped() {
71         local pid=$1
72         unset cleanup_pids[$pid]
73 }
74                                                                                 
75 commas() {
76         echo $* | sed -e 's/ /,/g'
77 }
78 do_bc_scale() {
79         local scale=$1
80         shift
81         echo "scale=$scale; $*" | bc
82 }
83 do_bc() {
84         do_bc_scale 10 $*
85 }
86 mean_stddev() {
87         local points=$*
88
89         local avg=0
90         local num=0
91         for p in $points; do
92                 avg=`do_bc $avg + $p`
93                 num=`do_bc $num + 1`
94         done
95         case $num in
96                 0) echo '??' ; return ;;
97                 1) echo "$avg:0" ; return ;;
98         esac
99
100         avg=`do_bc $avg / $num`
101         local tmp=0
102         for p in $points; do
103                 local dev=`do_bc \($p - $avg\) \^ 2`
104                 tmp=`do_bc $tmp + $dev`
105         done
106         tmp=`do_bc_scale 1 sqrt \( $tmp / \($num - 1\) \)`
107         avg=`do_bc_scale 1 $avg / 1`
108         echo "$avg:$tmp"
109 }
110
111 usage() {
112         echo $*
113         echo "       -b <block device to profile>"
114         echo "       -d <summary output directory>"
115         echo "       -l <max io len>"
116         echo "       -t <minimum number of threads per device>"
117         echo "       -T <maximum number of threads per device>"
118         echo "       -r <tests to run>"
119         exit;
120 }
121
122 # some cute code for handling tables whose columns fit
123 set_max() {
124         local target=$1
125         local val=$2
126                                                                                 
127         if [ $val -gt ${!target:-0} ]; then
128                 eval $target=$val
129         fi
130 }
131 table_set() {
132         local name="_table_$1"
133         local col=$2
134         local row=$3
135         local val=$4
136         local num
137                                                                                 
138         eval ${name}_${row}_${col}="'$val'"
139                                                                                 
140         set_max ${name}_${col}_longest ${#val}
141         set_max ${name}_num_col $(($col + 1))
142         set_max ${name}_num_row $(($row + 1))
143 }
144                                                                                 
145 table_get() {
146         local name="_table_$1"
147         local col=$2
148         local row=$3
149         tmp="${name}_${row}_${col}"
150         echo ${!tmp}
151 }
152                                                                                 
153 table_dump() {
154         local name="_table_$1"
155         local num_col;
156         local num_row;
157         local fmt="";
158         local tmp
159         local sep
160                                                                                 
161         tmp="${name}_num_col"
162         num_col="${!tmp:-0}"
163         tmp="${name}_num_row"
164         num_row="${!tmp:-0}"
165                                                                                 
166         # iterate through the columns to find the longest
167                                                                                 
168         sep=" "
169         for x in `seq 0 $num_col`; do
170                 tmp="${name}_${x}_longest"
171                 tmp=${!tmp:-0}
172                 [ $tmp -eq 0 ] && continue
173                                                                                 
174                 [ $x -eq $((num_col - 1)) ] && sep='\n'
175                                                                                 
176                 fmt="$fmt%-${tmp}s$sep"
177         done
178                                                                                 
179         # nothing in the table to print
180         [ -z "$fmt" ] && return
181                                                                                 
182         for y in `seq 0 $num_row`; do
183                 local row=""
184                 for x in `seq 0 $num_col`; do
185                                                                                 
186                         # skip this element if the column is empty
187                         tmp="${name}_${x}_longest"
188                         [ ${!tmp:-0} -eq 0 ] && continue
189                                                                                 
190                         # fill this cell with the value or '' for printf
191                         tmp="${name}_${y}_${x}"
192                         row="$row'${!tmp:-""}' "
193                 done
194                 eval printf "'$fmt'" $row
195         done
196 }
197
198 ######################################################################
199 # the sgp_dd tests
200 sgp_dd_banner() {
201         echo sgp_dd using dio=1 and thr=
202 }
203 sgp_dd_config() {
204         # it could be making sure that the block dev
205         # isn't in use by something else
206         local nothing=0
207 }
208 sgp_dd_prepare() {
209         if ! which sgp_dd; then
210                 echo "can't find sgp_dd binary"
211                 return 1
212         fi
213         return 0
214 }
215 sgp_dd_setup() {
216         # it could be making sure that the block dev
217         # isn't in use by something else
218         local nothing=0
219 }
220 sgp_dd_start() {
221         local threads=$1
222         local iosize=$2
223         local wor=$3
224         local i=$4
225         local ifof;
226         local bdev=${blocks[$i]};
227
228         case "$wor" in
229                 [wo]) ifof="if=/dev/zero of=$bdev" ;;
230                 r) ifof="if=$bdev of=/dev/null" ;;
231                 *) die "asked to do io with $wor?"
232         esac
233         echo sgp_dd $ifof bs=$iosize"k" count=$(($io_len / $iosize)) time=1 \
234                         dio=1 thr=$threads
235 }
236 sgp_dd_result() {
237         local output=$1
238
239         awk '($(NF) == "MB/sec") {print $(NF-1)}' < $output
240 }
241 sgp_dd_cleanup() {
242         # got me
243         local nothing=0
244 }
245 sgp_dd_finish() {
246         # got me
247         local nothing=0
248 }
249 sgp_dd_teardown() {
250         # got me
251         local nothing=0
252 }
253
254 ######################################################################
255 # the iozone tests
256 ext2_iozone_banner() {
257         echo "iozone -I on a clean ext2 fs"
258 }
259 ext2_iozone_config() {
260         local nothing=0
261 }
262 ext2_iozone_prepare() {
263         local index=$1
264         local bdev=${blocks[$index]}
265         local mntpnt=$tmpdir/mount_$index
266
267         if ! which iozone; then
268                 echo "iozone binary not found in PATH"
269                 return 1
270         fi
271         if ! iozone -i 0 -w -+o -s 1k -r 1k -f /dev/null > /dev/null; then
272                 echo "iozone doesn't support -+o"
273                 return 1
274         fi
275         if ! which mke2fs; then
276                 echo "mke2fs binary not found in PATH"
277                 return 1
278         fi
279
280         if ! mkdir -p $mntpnt ; then
281                 echo "$mntpnt isn't a directory?"
282         fi
283
284         echo making ext2 filesystem on $bdev
285         if ! mke2fs -b 4096 $bdev; then
286                 echo "mke2fs failed"
287                 return 1;
288         fi
289
290         if ! mount -t ext2 $bdev $mntpnt; then 
291                 echo "couldn't mount $bdev on $mntpnt"
292                 return 1;
293         fi
294
295         cleanup_mounts[$index]="$mntpnt"
296         return 0
297 }
298 ext2_iozone_setup() {
299         local id=$1
300         local wor=$2
301         local f="$tmpdir/mount_$id/iozone"
302
303         case "$wor" in
304                 w) rm -f $f ;;
305                 [or]) ;;
306                 *) die "asked to do io with $wor?"
307         esac
308 }
309 ext2_iozone_start() {
310         local threads=$1
311         local iosize=$2
312         local wor=$3
313         local id=$4
314         local args;
315         local f="$tmpdir/mount_$id/iozone"
316
317         case "$wor" in
318                 [wo]) args="-i 0 -w" ;;
319                 r) args="-i 1" ;;
320                 *) die "asked to do io with $wor?"
321         esac
322
323         echo iozone "$args -r ${iosize}k -s $(($io_len / $threads))k \
324                         -t $threads -+o -x -I -f $f"
325 }
326 ext2_iozone_result() {
327         local output=$1
328         local wor=$2
329         local string
330         local field
331
332         case "$wor" in
333                 [wo]) string="writers" 
334                    field=7 
335                         ;;
336                 r) string="readers" 
337                    field=6
338                         ;;
339                 *) die "asked to do io with $wor?"
340         esac
341
342         do_bc_scale 1 `awk '($1 == "Parent" && $'$field' == "'$string'") \
343                         {print $'$(($field + 2))'}' $output` / 1024
344 }
345 ext2_iozone_cleanup() {
346         # the final read w/o -w removed the file
347         local nothing=0
348 }
349 ext2_iozone_finish() {
350         local index=$1
351         local mntpnt=$tmpdir/mount_$index
352
353         umount -f $mntpnt
354         unset cleanup_mounts[$index]
355 }
356 ext2_iozone_teardown() {
357         local nothing=0
358 }
359
360 ######################################################################
361 # the lctl test_brw via the echo_client on top of the filter
362
363 # the echo_client setup is nutty enough to warrant its own clenaup
364 running_config=""
365 running_module=""
366 declare -a running_names
367 declare -a running_oids
368
369 cleanup_echo_filter() {
370         local i
371
372         for i in `seq 0 $last_block`; do
373                 [ -z "${running_oids[$i]}" ] && continue
374                 lctl --device "\$"echo_$i destroy ${running_oids[$i]} \
375                         $running_threads
376         done
377         unset running_oids
378
379         for n in ${running_names[*]}; do
380 # I can't believe leading whitespace matters here.
381 lctl << EOF
382 cfg_device $n
383 cleanup
384 detach
385 quit
386 EOF
387         done
388         unset running_names
389
390         for m in $running_module; do
391                 rmmod $m
392         done
393         running_module=""
394
395         [ ! -z "$running_config" ] && lconf --cleanup $running_config
396         running_config=""
397 }
398
399 echo_filter_banner() {
400         echo "test_brw on the echo_client on the filter" 
401 }
402 echo_filter_config() {
403         local index=$1
404         local bdev=${blocks[$index]}
405         local config="$tmpdir/config.xml"
406
407         if ! which lmc; then
408                 echo "lmc binary not found in PATH"
409                 return 1
410         fi
411         if ! which lconf; then
412                 echo "lconf binary not found in PATH"
413                 return 1
414         fi
415         if ! which lctl; then
416                 echo "lctl binary not found in PATH"
417                 return 1
418         fi
419
420         if [ $index = 0 ]; then
421                 if ! lmc -m $config --add net  \
422                         --node localhost --nid localhost --nettype tcp; then
423                         echo "error adding localhost net node"
424                         return 1
425                 fi
426         fi
427
428         if ! lmc -m $config --add ost --ost ost_$index --node localhost \
429                         --fstype ext3 --dev $bdev --journal_size 400; then
430                 echo "error adding $bdev to config with lmc"
431                 return 1
432         fi
433
434         # it would be nice to be able to ask lmc to setup an echo client
435         # to the filter here.  --add echo_client assumes osc
436 }
437 echo_filter_prepare() {
438         local index=$1
439         local bdev=${blocks[$index]}
440         local config="$tmpdir/config.xml"
441         local name="echo_$index"
442         local uuid="echo_$index_uuid"
443
444         if [ $index = 0 ]; then
445                 if ! lconf --reformat $config; then
446                         echo "error setting up with lconf"
447                         return 1;
448                 fi
449                 running_config="$config"
450                 if ! grep -q '^obdecho\>' /proc/modules; then
451                         local m
452                         if ! modprobe obdecho; then
453                                 if [ ! -z "$echo_module" ]; then
454                                         if ! insmod $echo_module; then
455                                                 echo "err: insmod $echo_module"
456                                                 return 1;
457                                         else
458                                                 m="$echo_module"
459                                         fi
460                                 else
461                                         echo "err: modprobe $obdecho"
462                                         return 1;
463                                 fi
464                         else
465                                 m=obdecho
466                         fi
467                         running_module=`basename $m | cut -d'.' -f 1`
468                 fi
469         fi
470
471 lctl << EOF
472         newdev
473         attach echo_client $name $uuid
474         setup ost_$index
475         quit
476 EOF
477         if [  $? != 0 ]; then
478                 echo "error setting up echo_client $name against ost_$index"
479                 return 1
480         fi
481         running_names[$index]=$name
482 }
483 echo_filter_setup() {
484         local id=$1
485         local wor=$2
486         local threads=$3
487         local name="echo_$id"
488         local oid
489
490         case "$wor" in
491                 w) ;;
492                 [or]) return ;;
493                 *) die "asked to do io with $wor?"
494         esac
495
496         running_threads=$threads
497         oid=`lctl --device "\$"$name create $threads | \
498                 awk '/ #1 is object id/ { print $6 }'`
499         # XXX need to deal with errors
500         running_oids[$id]=$oid
501 }
502 echo_filter_start() {
503         local threads=$1
504         local iosize=$2
505         local wor=$3
506         local id=$4
507         local rw
508
509         local name="echo_$id"
510         local len_pages=$(($io_len / $(($page_size / 1024)) / $threads ))
511         local size_pages=$(($iosize / $(($page_size / 1024)) ))
512
513         case "$wor" in
514                 [wo]) rw="w" ;;
515                 r) rw="r" ;;
516                 *) die "asked to do io with $wor?"
517         esac
518
519         echo lctl --threads $threads v "\$"$name \
520                 test_brw 1 $rw v $len_pages t${running_oids[$id]} p$size_pages
521 }
522 echo_filter_result() {
523         local output=$1
524         local total=0
525         local mbs
526
527         for mbs in `awk '($8=="MB/s):"){print substr($7,2)}' < $output`; do
528                 total=$(do_bc $total + $mbs)
529         done
530         do_bc_scale 2 $total / 1
531 }
532 echo_filter_cleanup() {
533         local id=$1
534         local wor=$2
535         local threads=$3
536         local name="echo_$id"
537
538         case "$wor" in
539                 [wo]) return ;;
540                 r) ;;
541                 *) die "asked to do io with $wor?"
542         esac
543
544         lctl --device "\$"$name destroy ${running_oids[$id]} $threads
545         unset running_oids[$id]
546 }
547 echo_filter_finish() {
548         local index=$1
549         # leave real work for _teardown
550 }
551 echo_filter_teardown() {
552         cleanup_echo_filter
553 }
554
555 ######################################################################
556 # the iteration that drives the tests
557
558 test_one() {
559         local test=$1
560         local my_x=$2
561         local threads=$3
562         local iosize=$4
563         local wor=$5
564         local vmstat_pid
565         local vmstat_log="$tmpdir/vmstat.log"
566         local opref="$test-$threads-$iosize-$wor"
567         local -a iostat_pids
568         # sigh.  but this makes it easier to dump into the tables
569         local -a read_req_s
570         local -a mb_s
571         local -a write_req_s
572         local -a sects_req
573         local -a queued_reqs
574         local -a service_ms
575
576         for i in `seq 0 $last_block`; do
577                 ${test}_setup $i $wor $threads
578         done
579
580         echo $test with $threads threads
581
582         $oprofile opcontrol --start
583
584         # start up vmstat and record its pid
585         nice -19 vmstat 1 > $vmstat_log 2>&1 &
586         [ $? = 0 ] || die "vmstat failed"
587         vmstat_pid=$!
588         pid_now_running $vmstat_pid
589
590         # start up each block device's iostat
591         for i in `seq 0 $last_block`; do
592                 nice -19 iostat -x ${blocks[$i]} 1 | awk \
593                         '($1 == "'${blocks[$i]}'"){print $0; fflush()}' \
594                         > $tmpdir/iostat.$i &
595                 local pid=$!
596                 pid_now_running $pid
597                 iostat_pids[$i]=$pid
598         done
599
600         $oprofile opcontrol --reset
601
602         # start all the tests.  each returns a pid to wait on
603         pids=""
604         for i in `seq 0 $last_block`; do
605                 local cmd=`${test}_start $threads $iosize $wor $i`
606                 $cmd > $tmpdir/$i 2>&1 &
607                 local pid=$!
608                 pids="$pids $pid"
609                 pid_now_running $pid
610         done
611
612         echo -n waiting on pids $pids:
613         for p in $pids; do
614                 wait $p
615                 echo -n .
616                 pid_has_stopped $p
617         done
618
619         # stop vmstat and all the iostats
620         kill $vmstat_pid
621         pid_has_stopped $vmstat_pid
622         for i in `seq 0 $last_block`; do
623                 local pid=${iostat_pids[$i]}
624                 [ -z "$pid" ] && continue
625
626                 kill $pid
627                 unset iostat_pids[$i]
628                 pid_has_stopped $pid
629         done
630
631         $oprofile opcontrol --shutdown
632         $oprofile opreport > $tmpdir/oprofile
633         echo >> $tmpdir/oprofile
634         $oprofile opreport -c -l | head -20 >> $tmpdir/oprofile
635         save_output $tmpdir/oprofile $opref.oprofile
636
637         # collect the results of vmstat and iostat
638         cpu=$(mean_stddev $(awk \
639               '(NR > 3 && NF == 16 && $16 != "id" )     \
640                 {print 100 - $16}' < $vmstat_log) )
641         save_output $vmstat_log $opref.vmstat
642
643         for i in `seq 0 $last_block`; do
644                 read_req_s[$i]=$(mean_stddev $(awk \
645                       '(NR > 1) {print $4}' < $tmpdir/iostat.$i) )
646                 write_req_s[$i]=$(mean_stddev $(awk \
647                       '(NR > 1) {print $5}' < $tmpdir/iostat.$i) )
648                 sects_req[$i]=$(mean_stddev $(awk \
649                       '(NR > 1) {print $10}' < $tmpdir/iostat.$i) )
650                 queued_reqs[$i]=$(mean_stddev $(awk \
651                       '(NR > 1) {print $11}' < $tmpdir/iostat.$i) )
652                 service_ms[$i]=$(mean_stddev $(awk \
653                       '(NR > 1) {print $13}' < $tmpdir/iostat.$i) )
654
655                 save_output $tmpdir/iostat.$i $opref.iostat.$i
656         done
657
658         # record each index's test results and sum them
659         thru=0
660         for i in `seq 0 $last_block`; do
661                 local t=`${test}_result $tmpdir/$i $wor`
662                 save_output $tmpdir/$i $opref.$i
663                 echo test returned "$t"
664                 mb_s[$i]="$t"
665                 # some tests return mean:stddev per device, filter out stddev
666                 thru=$(do_bc $thru + $(echo $t | sed -e 's/:.*$//g'))
667         done
668
669         for i in `seq 0 $last_block`; do
670                 ${test}_cleanup $i $wor $threads
671         done
672
673         # tabulate the results
674         echo $test did $thru mb/s with $cpu
675         table_set $test $my_x $cur_y `do_bc_scale 2 $thru / 1`
676         table_set $test $(($my_x + 1)) $cur_y $cpu
677
678         for i in `seq 0 $last_block`; do
679                 cur_y=$(($cur_y + 1))
680                 table_set $test $(($my_x)) $cur_y ${mb_s[$i]}
681                 table_set $test $(($my_x + 1)) $cur_y ${read_req_s[$i]}
682                 table_set $test $(($my_x + 2)) $cur_y ${write_req_s[$i]}
683                 table_set $test $(($my_x + 3)) $cur_y ${sects_req[$i]}
684                 table_set $test $(($my_x + 4)) $cur_y ${queued_reqs[$i]}
685                 table_set $test $(($my_x + 5)) $cur_y ${service_ms[$i]}
686         done
687
688         cur_y=$(($cur_y + 1))
689 }
690
691 test_iterator() {
692         local test=$1
693         local thr=$min_threads
694         local cleanup=""
695         local rc=0
696         local i
697         
698         for i in `seq 0 $last_block`; do
699                 if ! ${test}_config $i; then
700                         echo "couldn't config $test for bdev ${blocks[$i]}"
701                         echo "skipping $test for all block devices"
702                         cleanup=$(($i - 1))
703                         rc=1;
704                         break
705                 fi
706         done
707
708         for i in `seq 0 $last_block`; do
709                 # don't prepare if _config already failed
710                 [ ! -z "$cleanup" ] && break
711                 if ! ${test}_prepare $i; then
712                         echo "couldn't prepare $test for bdev ${blocks[$i]}"
713                         echo "skipping $test for all block devices"
714                         cleanup=$(($i - 1))
715                         rc=1;
716                         break
717                 fi
718         done
719
720         while [ -z "$cleanup" -a $thr -lt $(($max_threads + 1)) ]; do
721                 for iosize in 128 512; do
722                         table_set $test 0 $cur_y $thr
723                         table_set $test 1 $cur_y $iosize
724
725                         for wor in w o r; do
726                                 table_set $test 2 $cur_y $wor
727                                 test_one $test 3 $thr $iosize $wor
728                         done
729                 done
730                 thr=$(($thr + $thr))
731         done
732
733         [ -z "$cleanup" ] && cleanup=$last_block
734
735         if [ "$cleanup" != -1 ]; then
736                 for i in `seq $cleanup 0`; do
737                         ${test}_finish $i
738                 done
739         fi
740
741         ${test}_teardown
742
743         return $rc;
744 }
745
746 while getopts ":d:b:l:t:T:r:e:" opt; do
747         case $opt in
748                 e) echo_module=$OPTARG                 ;;
749                 b) block=$OPTARG                 ;;
750                 d) output_dir=$OPTARG                 ;;
751                 l) io_len=$OPTARG                       ;;
752                 r) run_tests=$OPTARG                    ;;
753                 t) min_threads=$OPTARG                  ;;
754                 T) max_threads=$OPTARG                  ;;
755                 \?) usage
756         esac
757 done
758
759 page_size=`getconf PAGE_SIZE` || die '"getconf PAGE_SIZE" failed'
760
761 [ ! -z "$echo_module" -a ! -f "$echo_module" ] && \
762         die "obdecho module $echo_module is not a file"
763
764 if [ -z "$io_len" ]; then
765         io_len=`awk '($1 == "MemTotal:"){print $2}' < /proc/meminfo`
766         [ -z "$io_len" ] && die "couldn't determine the amount of memory"
767 fi
768
769 if [ ! -z "$output_dir" ]; then
770         if [ ! -e "$output_dir" ]; then
771                  mkdir -p "$output_dir" || die  "error creating $output_dir"
772         fi
773         [ ! -d "$output_dir" ] && die "$output_dir isn't a directory"
774 fi
775
776 block=`echo $block | sed -e 's/,/ /g'`
777 [ -z "$block" ] && usage "need block devices"
778
779 run_tests=`echo $run_tests | sed -e 's/,/ /g'`
780 [ -z "$run_tests" ] && usage "need to specify tests to run with -r"
781 for t in $run_tests; do
782         if ! echo $possible_tests | grep -q $t ; then
783                 die "$t isn't one of the possible tests: $possible_tests"
784         fi
785 done
786
787 if which opcontrol; then
788         echo generating oprofile results
789         oprofile=""
790 else
791         echo not using oprofile
792         oprofile=": "
793 fi
794
795 [ $min_threads -gt $max_threads ] && \
796         die "min threads $min_threads must be <= min_threads $min_threads"
797
798 for b in $block; do
799         [ ! -e $b ] && die "block device file $b doesn't exist"
800         [ ! -b $b ] && die "$b isn't a block device"
801         dd if=$b of=/dev/null bs=8192 count=1 || \
802                 die "couldn't read 8k from $b, is it alive?"
803         [ ! -b $b ] && die "$b isn't a block device"
804         last_block=$(($last_block + 1))
805         blocks[$last_block]=$b
806 done    
807
808 tmpdir=`mktemp -d /tmp/.surveyXXXXXX` || die "couldn't create tmp dir"
809
810 echo each test will operate on $io_len"k"
811
812 test_results=""
813
814 for t in $run_tests; do
815
816         table_set $t 0 0 "T"
817         table_set $t 1 0 "L"
818         table_set $t 2 0 "m"
819         table_set $t 3 0 "A"
820         table_set $t 4 0 "C"
821         table_set $t 3 1 "MB"
822         table_set $t 4 1 "rR"
823         table_set $t 5 1 "wR"
824         table_set $t 6 1 "SR"
825         table_set $t 7 1 "Q"
826         table_set $t 8 1 "ms"
827         cur_y=2;
828
829         if ! test_iterator $t; then
830                 continue;
831         fi
832         test_results="$test_results $t"
833 done
834
835 [ ! -z "$test_results" ] && (
836         echo
837         echo "T = number of concurrent threads per device"
838         echo "L = base io operation length, in KB"
839         echo "m = IO method: read, write, or over-write"
840         echo "A = aggregate throughput from all devices"
841         echo "C = percentage CPU used, both user and system"
842         echo "MB/s = per-device throughput"
843         echo "rR = read requests issued to the device per second"
844         echo "wR = write requests issued to the device per second"
845         echo "SR = sectors per request; sectors tend to be 512 bytes"
846         echo "Q = the average number of requests queued on the device"
847         echo "ms = the average ms taken by the device to service a req"
848         echo
849         echo "foo:bar represents a mean of foo with a stddev of bar"
850 )
851
852 for t in $test_results; do
853         ${t}_banner
854         table_dump $t
855 done