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