Whamcloud - gitweb
72ea7dbc2c698f8dab75938dfb20d99db164c8c6
[fs/lustre-release.git] / lustre / tests / functions.sh
1 #!/bin/bash
2
3 # Simple function used by run_*.sh scripts
4
5 assert_env() {
6     local failed=""
7     for name in $@; do
8         if [ -z "${!name}" ]; then
9             echo "$0: $name must be set"
10             failed=1
11         fi
12     done
13     [ $failed ] && exit 1 || true
14 }
15
16 # lrepl - Lustre test Read-Eval-Print Loop.
17 #
18 # This function implements a REPL for the Lustre test framework.  It
19 # doesn't exec an actual shell because the user may want to inspect
20 # variables and use functions from the test framework.
21 lrepl() {
22     local line
23     local rawline
24     local prompt
25
26     cat <<EOF
27         This is an interactive read-eval-print loop interactive shell
28         simulation that you can use to debug failing tests.  You can
29         enter most bash command lines (see notes below).
30
31         Use this REPL to inspect variables, set them, call test
32         framework shell functions, etcetera.
33
34         'exit' or EOF to exit this shell.
35
36         set \$retcode to 0 to cause the assertion failure that
37         triggered this REPL to be ignored.
38
39         Examples:
40             do_facet ost1 lctl get_param ost.*.ost.threads_*
41             do_rpc_nodes \$OSTNODES unload_modules
42
43         NOTES:
44             All but the last line of multi-line statements or blocks
45             must end in a backslash.
46
47             "Here documents" are not supported.
48
49             History is not supported, but command-line editing is.
50
51 EOF
52
53     # Prompt escapes don't work in read -p, sadly.
54     prompt=":test_${testnum:-UNKNOWN}:$(uname -n):$(basename $PWD)% "
55
56     # We use read -r to get close to a shell experience
57     while read -e -r -p "$prompt" rawline; do
58         line=
59         case "$rawline" in
60         # Don't want to exit-exit, just exit the REPL
61         exit) break;;
62         # We need to handle continuations, and read -r doesn't do
63         # that for us.  Yet we need read -r.
64         #
65         # We also use case/esac to compare lines read to "*\\"
66         # because [ "$line" = *\\ ] and variants of that don't work.
67         *\\) line="$rawline"
68             while read -e -r -p '> ' rawline
69             do
70                 line="$line"$'\n'"$rawline"
71                 case "$rawline" in
72                 # We could check for here documents by matching
73                 # against *<<*, but who cares.
74                 *\\) continue;;
75                 *) break;;
76                 esac
77             done
78             ;;
79         *) line=$rawline
80         esac
81
82         case "$line" in
83         *\\) break;;
84         esac
85
86         # Finally!  Time to eval.
87         eval "$line"
88     done
89
90     echo $'\n\tExiting interactive shell...\n'
91     return 0
92 }
93
94 # lassert - Lustre test framework assert
95 #
96 # Arguments: failure code, failure message, expression/statement
97 #
98 # lassert evaluates the expression given, and, if false, calls
99 # error() to trigger test failure.  If REPL_ON_LASSERT is true then
100 # lassert will call lrepl() to give the user an interactive shell.
101 # If the REPL sets retcode=0 then the assertion failure will be
102 # ignored.
103 lassert() {
104     local retcode=$1
105     local msg=$2
106     shift 2
107
108     echo "checking $* ($(eval echo \""$*"\"))..."
109     eval "$@" && return 0;
110
111     if ${REPL_ON_LASSERT:-false}; then
112         echo "Assertion $retcode failed: $* (expanded: $(eval echo \""$*"\"))
113 $msg"
114         lrepl
115     fi
116
117     error "Assertion $retcode failed: $* (expanded: $(eval echo \""$*"\"))
118 $msg"
119     return $retcode
120 }
121
122 # setmodopts- set module options for subsequent calls to load_modules
123 #
124 # Usage: setmodopts module_name new_value [var_in_which_to_save_old_value]
125 #        setmodopts -a module_name new_value [var_in_which_to_save_old_value]
126 #
127 # In the second usage the new value is appended to the old.
128 setmodopts() {
129         local _append=false
130
131         if [ "$1" = -a ]; then
132             _append=true
133             shift
134         fi
135
136         local _var=MODOPTS_$1
137         local _newvalue=$2
138         local _savevar=$3
139         local _oldvalue
140
141         # Dynamic naming of variables is a pain in bash.  In ksh93 we could
142         # write "nameref opts_var=${modname}_MODOPTS" then assign directly
143         # to opts_var.  Associative arrays would also help, alternatively.
144         # Alas, we're stuck with eval until all distros move to a more recent
145         # version of bash.  Fortunately we don't need to eval unset and export.
146
147         if [ -z "$_newvalue" ]; then
148             unset $_var
149             return 0
150         fi
151
152         _oldvalue=${!var}
153         $_append && _newvalue="$_oldvalue $_newvalue"
154         export $_var="$_newvalue"
155         echo setmodopts: ${_var}=${_newvalue}
156
157         [ -n "$_savevar" ] && eval $_savevar=\""$_oldvalue"\"
158 }
159
160 echoerr () { echo "$@" 1>&2 ; }
161
162 signaled() {
163     echoerr "$(date +'%F %H:%M:%S'): client load was signaled to terminate"
164
165     local PGID=$(ps -eo "%c %p %r" | awk "/ $PPID / {print \$3}")
166     kill -TERM -$PGID
167     sleep 5
168     kill -KILL -$PGID
169 }
170
171 mpi_run () {
172     local mpirun="$MPIRUN $MPIRUN_OPTIONS"
173     local command="$mpirun $@"
174     local mpilog=$TMP/mpi.log
175     local rc
176
177     if [ -n "$MPI_USER" -a "$MPI_USER" != root -a -n "$mpirun" ]; then
178         echo "+ chmod 0777 $MOUNT"
179         chmod 0777 $MOUNT
180         command="su $MPI_USER sh -c \"$command \""
181     fi
182
183     ls -ald $MOUNT
184     echo "+ $command"
185     eval $command 2>&1 | tee $mpilog || true
186
187     rc=${PIPESTATUS[0]}
188     if [ $rc -eq 0 ] && grep -q "p4_error:" $mpilog ; then
189        rc=1
190     fi
191     return $rc
192 }
193
194 nids_list () {
195         local list
196         local escape="$2"
197         for i in ${1//,/ }; do
198                 if [ "$list" = "" ]; then
199                         list="$i@$NETTYPE"
200                 else
201                         list="$list$escape $i@$NETTYPE"
202                 fi
203         done
204         echo $list
205 }
206
207 # FIXME: all setup/cleanup can be done without rpc.sh
208 lst_end_session () {
209     local verbose=false
210     [ x$1 = x--verbose ] && verbose=true
211
212     export LST_SESSION=`$LST show_session 2>/dev/null | awk -F " " '{print $5}'`
213     [ "$LST_SESSION" == "" ] && return
214
215         $LST stop b
216     if $verbose; then
217         $LST show_error c s
218     fi
219     $LST end_session
220 }
221
222 lst_session_cleanup_all () {
223     local list=$(comma_list $(nodes_list))
224     do_rpc_nodes $list lst_end_session
225 }
226
227 lst_cleanup () {
228     lsmod | grep -q lnet_selftest && \
229         rmmod lnet_selftest > /dev/null 2>&1 || true
230 }
231
232 lst_cleanup_all () {
233    local list=$(comma_list $(nodes_list))
234
235    # lst end_session needs to be executed only locally
236    # i.e. on node where lst new_session was called
237    lst_end_session --verbose
238    do_rpc_nodes $list lst_cleanup
239 }
240
241 lst_setup () {
242     load_module lnet_selftest
243 }
244
245 lst_setup_all () {
246     local list=$(comma_list $(nodes_list))
247     do_rpc_nodes $list lst_setup
248 }
249
250 ###
251 # short_hostname
252 #
253 # Passed a single argument, strips everything off following
254 # and includes the first period.
255 # client-20.lab.whamcloud.com becomes client-20
256 short_hostname() {
257   echo $(sed 's/\..*//' <<< $1)
258 }
259
260 ###
261 # short_nodename
262 #
263 # Find remote nodename, stripped of any domain, etc.
264 # 'hostname -s' is easy, but not implemented on all systems
265 short_nodename() {
266         local rname=$(do_node $1 "uname -n" || echo -1)
267         if [[ "$rname" = "-1" ]]; then
268                 rname=$1
269         fi
270         echo $(short_hostname $rname)
271 }
272
273 print_opts () {
274     local var
275
276     echo OPTIONS:
277
278     for i in $@; do
279         var=$i
280         echo "${var}=${!var}"
281     done
282     [ -e $MACHINEFILE ] && cat $MACHINEFILE
283 }
284
285 run_compilebench() {
286         local dir=${1:-$DIR}
287         local cbench_DIR=${cbench_DIR:-""}
288         local cbench_IDIRS=${cbench_IDIRS:-2}
289         local cbench_RUNS=${cbench_RUNS:-2}
290
291         print_opts cbench_DIR cbench_IDIRS cbench_RUNS
292
293         [ x$cbench_DIR = x ] &&
294                 { skip_env "compilebench not found" && return; }
295
296         [ -e $cbench_DIR/compilebench ] ||
297                 { skip_env "No compilebench build" && return; }
298
299         # Space estimation:
300         # compile dir kernel-0  ~1GB
301         # required space        ~1GB * cbench_IDIRS
302         local space=$(df -P $dir | tail -n 1 | awk '{ print $4 }')
303         if [[ $space -le $((1024 * 1024 * cbench_IDIRS)) ]]; then
304                 cbench_IDIRS=$((space / 1024 / 1024))
305                 [[ $cbench_IDIRS -eq 0 ]] &&
306                         skip_env "Need free space at least 1GB, have $space" &&
307                         return
308
309                 echo "reducing initial dirs to $cbench_IDIRS"
310         fi
311         echo "free space = $space KB"
312
313         # FIXME:
314         # t-f _base needs to be modifyed to set properly tdir
315         # for new "test_foo" functions names
316         # local testdir=$DIR/$tdir
317         local testdir=$dir/d0.compilebench.$$
318         mkdir -p $testdir
319
320     local savePWD=$PWD
321     cd $cbench_DIR
322     local cmd="./compilebench -D $testdir -i $cbench_IDIRS \
323         -r $cbench_RUNS --makej"
324
325     log "$cmd"
326
327     local rc=0
328     eval $cmd
329     rc=$?
330
331     cd $savePWD
332     [ $rc = 0 ] || error "compilebench failed: $rc"
333     rm -rf $testdir
334 }
335
336 run_metabench() {
337         local dir=${1:-$DIR}
338         local mntpt=${2:-$MOUNT}
339         METABENCH=${METABENCH:-$(which metabench 2> /dev/null || true)}
340         mbench_NFILES=${mbench_NFILES:-30400}
341         # threads per client
342         mbench_THREADS=${mbench_THREADS:-4}
343         mbench_OPTIONS=${mbench_OPTIONS:-}
344         mbench_CLEANUP=${mbench_CLEANUP:-true}
345
346         [ x$METABENCH = x ] &&
347                 { skip_env "metabench not found" && return; }
348
349         print_opts METABENCH clients mbench_NFILES mbench_THREADS
350
351         local testdir=$dir/d0.metabench
352         mkdir -p $testdir
353         # mpi_run uses mpiuser
354         chmod 0777 $testdir
355
356         # -C             Run the file creation tests. Creates zero byte files.
357         # -S             Run the file stat tests.
358         # -c nfile       Number of files to be used in each test.
359         # -k             Cleanup files when finished.
360         local cmd="$METABENCH -w $testdir -c $mbench_NFILES -C -S $mbench_OPTIONS"
361         echo "+ $cmd"
362
363         # find out if we need to use srun by checking $SRUN_PARTITION
364         if [ "$SRUN_PARTITION" ]; then
365                 $SRUN $SRUN_OPTIONS -D $testdir -w $clients -N $num_clients \
366                         -n $((num_clients * mbench_THREADS)) \
367                         -p $SRUN_PARTITION -- $cmd
368         else
369                 mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
370                         -np $((num_clients * $mbench_THREADS)) $cmd
371         fi
372
373         local rc=$?
374         if [ $rc != 0 ] ; then
375                 error "metabench failed! $rc"
376         fi
377
378         if $mbench_CLEANUP; then
379                 rm -rf $testdir
380         else
381                 mv $dir/d0.metabench $mntpt/_xxx.$(date +%s).d0.metabench
382         fi
383 }
384
385 run_simul() {
386
387     SIMUL=${SIMUL:=$(which simul 2> /dev/null || true)}
388     # threads per client
389     simul_THREADS=${simul_THREADS:-2}
390     simul_REP=${simul_REP:-20}
391
392     if [ "$NFSCLIENT" ]; then
393         skip "skipped for NFSCLIENT mode"
394         return
395     fi
396
397     [ x$SIMUL = x ] &&
398         { skip_env "simul not found" && return; }
399
400     # FIXME
401     # Need space estimation here.
402
403     print_opts SIMUL clients simul_REP simul_THREADS
404
405     local testdir=$DIR/d0.simul
406     mkdir -p $testdir
407     # mpi_run uses mpiuser
408     chmod 0777 $testdir
409
410     # -n # : repeat each test # times
411     # -N # : repeat the entire set of tests # times
412
413     local cmd="$SIMUL -d $testdir -n $simul_REP -N $simul_REP"
414
415         echo "+ $cmd"
416         # find out if we need to use srun by checking $SRUN_PARTITION
417         if [ "$SRUN_PARTITION" ]; then
418                 $SRUN $SRUN_OPTIONS -D $testdir -w $clients -N $num_clients \
419                         -n $((num_clients * simul_THREADS)) -p $SRUN_PARTITION \
420                         -- $cmd
421         else
422                 mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
423                         -np $((num_clients * simul_THREADS)) $cmd
424         fi
425
426     local rc=$?
427     if [ $rc != 0 ] ; then
428         error "simul failed! $rc"
429     fi
430     rm -rf $testdir
431 }
432
433 run_mdtest() {
434
435     MDTEST=${MDTEST:=$(which mdtest 2> /dev/null || true)}
436     # threads per client
437     mdtest_THREADS=${mdtest_THREADS:-2}
438     mdtest_nFiles=${mdtest_nFiles:-"100000"}
439     # We devide the files by number of core
440     mdtest_nFiles=$((mdtest_nFiles/mdtest_THREADS/num_clients))
441     mdtest_iteration=${mdtest_iteration:-1}
442         local mdtest_custom_params=${mdtest_custom_params:-""}
443
444     local type=${1:-"ssf"}
445
446     if [ "$NFSCLIENT" ]; then
447         skip "skipped for NFSCLIENT mode"
448         return
449     fi
450
451     [ x$MDTEST = x ] &&
452         { skip_env "mdtest not found" && return; }
453
454     # FIXME
455     # Need space estimation here.
456
457     print_opts MDTEST mdtest_iteration mdtest_THREADS mdtest_nFiles
458
459     local testdir=$DIR/d0.mdtest
460     mkdir -p $testdir
461     # mpi_run uses mpiuser
462     chmod 0777 $testdir
463
464     # -i # : repeat each test # times
465     # -d   : test dir
466     # -n # : number of file/dir to create/stat/remove
467     # -u   : each process create/stat/remove individually
468
469         local cmd="$MDTEST -d $testdir -i $mdtest_iteration \
470                 -n $mdtest_nFiles $mdtest_custom_params"
471
472         [ $type = "fpp" ] && cmd="$cmd -u"
473
474         echo "+ $cmd"
475         # find out if we need to use srun by checking $SRUN_PARTITION
476         if [ "$SRUN_PARTITION" ]; then
477                 $SRUN $SRUN_OPTIONS -D $testdir -w $clients -N $num_clients \
478                         -n $((num_clients * mdtest_THREADS)) \
479                         -p $SRUN_PARTITION -- $cmd
480         else
481                 mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
482                         -np $((num_clients * mdtest_THREADS)) $cmd
483         fi
484
485     local rc=$?
486     if [ $rc != 0 ] ; then
487         error "mdtest failed! $rc"
488     fi
489     rm -rf $testdir
490 }
491
492 run_connectathon() {
493         local dir=${1:-$DIR}
494         cnt_DIR=${cnt_DIR:-""}
495         cnt_NRUN=${cnt_NRUN:-10}
496
497         print_opts cnt_DIR cnt_NRUN
498
499         [ x$cnt_DIR = x ] &&
500                 { skip_env "connectathon dir not found" && return; }
501
502         [ -e $cnt_DIR/runtests ] ||
503                 { skip_env "No connectathon runtests found" && return; }
504
505         # Space estimation:
506         # "special" tests create a 30 MB file + misc. small files
507         # required space ~40 MB
508         local space=$(df -P $dir | tail -n 1 | awk '{ print $4 }')
509         if [[ $space -le $((1024 * 40)) ]]; then
510                 skip_env "Need free space at least 40MB, have $space KB" &&
511                 return
512         fi
513         echo "free space = $space KB"
514
515         local testdir=$dir/d0.connectathon
516         mkdir -p $testdir
517
518         local savePWD=$PWD
519         cd $cnt_DIR
520
521         #
522         # To run connectathon:
523         # runtests [-a|-b|-g|-s|-l] [-f|-n|-t] [-N numpasses] [test-directory]
524         #
525         # One of the following test types
526         #    -b  basic
527         #    -g  general
528         #    -s  special
529         #    -l  lock
530         #    -a  all of the above
531         #
532         # -f  a quick functional test
533         # -n  suppress directory operations (mkdir and rmdir)
534         # -t  run with time statistics (default for basic tests)
535         #
536         # -N numpasses - specifies the number of times to run
537         #                the tests. Optional.
538
539         tests="-b -g -s"
540         # Include lock tests unless we're running on nfsv4
541         local fstype=$(df -TP $testdir | awk 'NR==2  {print $2}')
542         echo "$testdir: $fstype"
543         if [[ $fstype != "nfs4" ]]; then
544                 tests="$tests -l"
545         fi
546         echo "tests: $tests"
547         for test in $tests; do
548                 local cmd="./runtests -N $cnt_NRUN $test -f $testdir"
549                 local rc=0
550
551                 log "$cmd"
552                 eval $cmd
553                 rc=$?
554                 [ $rc = 0 ] || error "connectathon failed: $rc"
555         done
556
557         cd $savePWD
558         rm -rf $testdir
559 }
560
561 run_ior() {
562         local type=${1:="ssf"}
563         local dir=${2:-$DIR}
564         local testdir=$dir/d0.ior.$type
565         local nfs_srvmntpt=$3
566
567         if [ "$NFSCLIENT" ]; then
568                 [[ -n $nfs_srvmntpt ]] ||
569                         { error "NFSCLIENT mode, but nfs exported dir"\
570                                 "is not set!" && return 1; }
571         fi
572
573         IOR=${IOR:-$(which IOR 2> /dev/null || true)}
574         [ x$IOR = x ] &&
575                 { skip_env "IOR not found" && return; }
576
577         # threads per client
578         ior_THREADS=${ior_THREADS:-2}
579         ior_iteration=${ior_iteration:-1}
580         ior_blockSize=${ior_blockSize:-6}
581         ior_blockUnit=${ior_blockUnit:-M}   # K, M, G
582         ior_xferSize=${ior_xferSize:-1M}
583         ior_type=${ior_type:-POSIX}
584         ior_DURATION=${ior_DURATION:-30}        # minutes
585         local multiplier=1
586         case ${ior_blockUnit} in
587                 [G])
588                         multiplier=$((1024 * 1024 * 1024))
589                         ;;
590                 [M])
591                         multiplier=$((1024 * 1024))
592                         ;;
593                 [K])
594                         multiplier=1024
595                         ;;
596                 *)      error "Incorrect block unit should be one of [KMG]"
597                         ;;
598         esac
599
600         # calculate the space in bytes
601         local space=$(df -B 1 -P $dir | tail -n 1 | awk '{ print $4 }')
602         local total_threads=$((num_clients * ior_THREADS))
603         echo "+ $ior_blockSize * $multiplier * $total_threads "
604         if [ $((space / 2)) -le \
605              $((ior_blockSize * multiplier * total_threads)) ]; then
606                 ior_blockSize=$((space / 2 / multiplier / total_threads))
607                 [ $ior_blockSize -eq 0 ] && \
608                 skip_env "Need free space more than $((2 * total_threads)) \
609                          ${ior_blockUnit}: have $((space / multiplier))" &&
610                          return
611
612                 echo "(reduced blockSize to $ior_blockSize \
613                      ${ior_blockUnit} bytes)"
614         fi
615
616         print_opts IOR ior_THREADS ior_DURATION MACHINEFILE
617
618         mkdir -p $testdir
619         # mpi_run uses mpiuser
620         chmod 0777 $testdir
621         if [ -z "$NFSCLIENT" ]; then
622                 ior_stripe_params=${ior_stripe_params:-"-c -1"}
623                 $LFS setstripe $testdir $ior_stripe_params ||
624                         { error "setstripe failed" && return 2; }
625         fi
626
627         #
628         # -b N  blockSize --
629         #       contiguous bytes to write per task (e.g.: 8, 4K, 2M, 1G)"
630         # -o S  testFileName
631         # -t N  transferSize -- size of transfer in bytes (e.g.: 8, 4K, 2M, 1G)"
632         # -w    writeFile -- write file"
633         # -r    readFile -- read existing file"
634         # -W    checkWrite -- check read after write"
635         # -C    reorderTasks -- changes task ordering to n+1 ordering for readback
636         # -T    maxTimeDuration -- max time in minutes to run tests"
637         # -k    keepFile -- keep testFile(s) on program exit
638
639         local cmd
640         if [ -n "$ior_custom_params" ]; then
641                 cmd="$IOR $ior_custom_params -o $testdir/iorData"
642         else
643                 cmd="$IOR -a $ior_type -b ${ior_blockSize}${ior_blockUnit} \
644                 -o $testdir/iorData -t $ior_xferSize -v -C -w -r -W \
645                 -i $ior_iteration -T $ior_DURATION -k"
646         fi
647
648         [ $type = "fpp" ] && cmd="$cmd -F"
649
650         echo "+ $cmd"
651         # find out if we need to use srun by checking $SRUN_PARTITION
652         if [ "$SRUN_PARTITION" ]; then
653                 $SRUN $SRUN_OPTIONS -D $testdir -w $clients -N $num_clients \
654                         -n $((num_clients * ior_THREADS)) -p $SRUN_PARTITION \
655                         -- $cmd
656         else
657                 mpi_ior_custom_threads=${mpi_ior_custom_threads:-"$((num_clients * ior_THREADS))"}
658                 mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
659                         -np $mpi_ior_custom_threads $cmd
660         fi
661
662     local rc=$?
663     if [ $rc != 0 ] ; then
664         error "ior failed! $rc"
665     fi
666     rm -rf $testdir
667 }
668
669 run_mib() {
670
671     MIB=${MIB:=$(which mib 2> /dev/null || true)}
672     # threads per client
673     mib_THREADS=${mib_THREADS:-2}
674     mib_xferSize=${mib_xferSize:-1m}
675     mib_xferLimit=${mib_xferLimit:-5000}
676     mib_timeLimit=${mib_timeLimit:-300}
677
678     if [ "$NFSCLIENT" ]; then
679         skip "skipped for NFSCLIENT mode"
680         return
681     fi
682
683     [ x$MIB = x ] &&
684         { skip_env "MIB not found" && return; }
685
686     print_opts MIB mib_THREADS mib_xferSize mib_xferLimit mib_timeLimit \
687         MACHINEFILE
688
689     local testdir=$DIR/d0.mib
690     mkdir -p $testdir
691     # mpi_run uses mpiuser
692     chmod 0777 $testdir
693     $LFS setstripe $testdir -c -1 ||
694         { error "setstripe failed" && return 2; }
695     #
696     # -I    Show intermediate values in output
697     # -H    Show headers in output
698     # -L    Do not issue new system calls after this many seconds
699     # -s    Use system calls of this size
700     # -t    test dir
701     # -l    Issue no more than this many system calls
702     local cmd="$MIB -t $testdir -s $mib_xferSize -l $mib_xferLimit \
703         -L $mib_timeLimit -HI -p mib.$(date +%Y%m%d%H%M%S)"
704
705         echo "+ $cmd"
706         # find out if we need to use srun by checking $SRUN_PARTITION
707         if [ "$SRUN_PARTITION" ]; then
708                 $SRUN $SRUN_OPTIONS -D $testdir -w $clients -N $num_clients \
709                         -n $((num_clients * mib_THREADS)) -p $SRUN_PARTITION \
710                         -- $cmd
711         else
712                 mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
713                         -np $((num_clients * mib_THREADS)) $cmd
714         fi
715
716     local rc=$?
717     if [ $rc != 0 ] ; then
718         error "mib failed! $rc"
719     fi
720     rm -rf $testdir
721 }
722
723 run_cascading_rw() {
724
725     CASC_RW=${CASC_RW:-$(which cascading_rw 2> /dev/null || true)}
726     # threads per client
727     casc_THREADS=${casc_THREADS:-2}
728     casc_REP=${casc_REP:-300}
729
730     if [ "$NFSCLIENT" ]; then
731         skip "skipped for NFSCLIENT mode"
732         return
733     fi
734
735     [ x$CASC_RW = x ] &&
736         { skip_env "cascading_rw not found" && return; }
737
738     # FIXME
739     # Need space estimation here.
740
741     print_opts CASC_RW clients casc_THREADS casc_REP MACHINEFILE
742
743     local testdir=$DIR/d0.cascading_rw
744     mkdir -p $testdir
745     # mpi_run uses mpiuser
746     chmod 0777 $testdir
747
748     # -g: debug mode
749     # -n: repeat test # times
750
751     local cmd="$CASC_RW -g -d $testdir -n $casc_REP"
752
753         echo "+ $cmd"
754         mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
755                 -np $((num_clients * $casc_THREADS)) $cmd
756
757     local rc=$?
758     if [ $rc != 0 ] ; then
759         error "cascading_rw failed! $rc"
760     fi
761     rm -rf $testdir
762 }
763
764 run_write_append_truncate() {
765
766     # threads per client
767     write_THREADS=${write_THREADS:-8}
768     write_REP=${write_REP:-10000}
769
770     if [ "$NFSCLIENT" ]; then
771         skip "skipped for NFSCLIENT mode"
772         return
773     fi
774
775     # location is lustre/tests dir
776     if ! which write_append_truncate > /dev/null 2>&1 ; then
777         skip_env "write_append_truncate not found"
778         return
779     fi
780
781     # FIXME
782     # Need space estimation here.
783
784     local testdir=$DIR/d0.write_append_truncate
785     local file=$testdir/f0.wat
786
787     print_opts clients write_REP write_THREADS MACHINEFILE
788
789     mkdir -p $testdir
790     # mpi_run uses mpiuser
791     chmod 0777 $testdir
792
793     local cmd="write_append_truncate -n $write_REP $file"
794
795         echo "+ $cmd"
796         mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
797                 -np $((num_clients * $write_THREADS)) $cmd
798
799     local rc=$?
800     if [ $rc != 0 ] ; then
801         error "write_append_truncate failed! $rc"
802         return $rc
803     fi
804     rm -rf $testdir
805 }
806
807 run_write_disjoint() {
808         if [ "$NFSCLIENT" ]; then
809                 skip "skipped for NFSCLIENT mode"
810                 return
811         fi
812
813         WRITE_DISJOINT=${WRITE_DISJOINT:-$(which write_disjoint 2> /dev/null ||
814                                            true)}
815
816         [ x$WRITE_DISJOINT = x ] &&
817                 { skip_env "write_disjoint not found" && return; }
818
819         # threads per client
820         wdisjoint_THREADS=${wdisjoint_THREADS:-4}
821         wdisjoint_REP=${wdisjoint_REP:-10000}
822         chunk_size_limit=$1
823
824     # FIXME
825     # Need space estimation here.
826
827     print_opts WRITE_DISJOINT clients wdisjoint_THREADS wdisjoint_REP \
828         MACHINEFILE
829     local testdir=$DIR/d0.write_disjoint
830     mkdir -p $testdir
831     # mpi_run uses mpiuser
832     chmod 0777 $testdir
833
834         local cmd="$WRITE_DISJOINT -f $testdir/file -n $wdisjoint_REP -m \
835                         $chunk_size_limit"
836
837         echo "+ $cmd"
838         mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
839                 -np $((num_clients * $wdisjoint_THREADS)) $cmd
840
841     local rc=$?
842     if [ $rc != 0 ] ; then
843         error "write_disjoint failed! $rc"
844     fi
845     rm -rf $testdir
846 }
847
848 run_parallel_grouplock() {
849
850     PARALLEL_GROUPLOCK=${PARALLEL_GROUPLOCK:-$(which parallel_grouplock \
851         2> /dev/null || true)}
852     parallel_grouplock_MINTASKS=${parallel_grouplock_MINTASKS:-5}
853
854     if [ "$NFSCLIENT" ]; then
855         skip "skipped for NFSCLIENT mode"
856         return
857     fi
858
859     [ x$PARALLEL_GROUPLOCK = x ] &&
860         { skip "PARALLEL_GROUPLOCK not found" && return; }
861
862     print_opts clients parallel_grouplock_MINTASKS MACHINEFILE
863
864     local testdir=$DIR/d0.parallel_grouplock
865     mkdir -p $testdir
866     # mpi_run uses mpiuser
867     chmod 0777 $testdir
868
869     local cmd
870     local status=0
871     local subtest
872         for i in $(seq 12); do
873                 subtest="-t $i"
874                 local cmd="$PARALLEL_GROUPLOCK -g -v -d $testdir $subtest"
875                 echo "+ $cmd"
876
877                 mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
878                         -np $parallel_grouplock_MINTASKS $cmd
879                 local rc=$?
880                 if [ $rc != 0 ] ; then
881                         error_noexit "parallel_grouplock subtests $subtest " \
882                                      "failed! $rc"
883                 else
884                         echo "parallel_grouplock subtests $subtest PASS"
885                 fi
886                 let status=$((status + rc))
887                 # clear debug to collect one log per one test
888                 do_nodes $(comma_list $(nodes_list)) lctl clear
889         done
890         [ $status -eq 0 ] || error "parallel_grouplock status: $status"
891         rm -rf $testdir
892 }
893
894 cleanup_statahead () {
895     trap 0
896
897     local clients=$1
898     local mntpt_root=$2
899     local num_mntpts=$3
900
901     for i in $(seq 0 $num_mntpts);do
902         zconf_umount_clients $clients ${mntpt_root}$i ||
903             error_exit "Failed to umount lustre on ${mntpt_root}$i"
904     done
905 }
906
907 run_statahead () {
908
909     statahead_NUMMNTPTS=${statahead_NUMMNTPTS:-5}
910     statahead_NUMFILES=${statahead_NUMFILES:-500000}
911
912     if [[ -n $NFSCLIENT ]]; then
913         skip "Statahead testing is not supported on NFS clients."
914         return 0
915     fi
916
917     [ x$MDSRATE = x ] &&
918         { skip_env "mdsrate not found" && return; }
919
920     print_opts MDSRATE clients statahead_NUMMNTPTS statahead_NUMFILES
921
922     # create large dir
923
924     # do not use default "d[0-9]*" dir name
925     # to avoid of rm $statahead_NUMFILES (500k) files in t-f cleanup
926     local dir=dstatahead
927     local testdir=$DIR/$dir
928
929     # cleanup only if dir exists
930     # cleanup only $statahead_NUMFILES number of files
931     # ignore the other files created by someone else
932     [ -d $testdir ] &&
933         mdsrate_cleanup $((num_clients * 32)) $MACHINEFILE \
934             $statahead_NUMFILES $testdir 'f%%d' --ignore
935
936     mkdir -p $testdir
937     # mpi_run uses mpiuser
938     chmod 0777 $testdir
939
940     local num_files=$statahead_NUMFILES
941
942     local IFree=$(inodes_available)
943     if [ $IFree -lt $num_files ]; then
944       num_files=$IFree
945     fi
946
947     cancel_lru_locks mdc
948
949     local cmd1="${MDSRATE} ${MDSRATE_DEBUG} --mknod --dir $testdir"
950     local cmd2="--nfiles $num_files --filefmt 'f%%d'"
951     local cmd="$cmd1 $cmd2"
952     echo "+ $cmd"
953
954         mpi_run ${MACHINEFILE_OPTION} ${MACHINEFILE} \
955                 -np $((num_clients * 32)) $cmd
956
957     local rc=$?
958     if [ $rc != 0 ] ; then
959         error "mdsrate failed to create $rc"
960         return $rc
961     fi
962
963     local num_mntpts=$statahead_NUMMNTPTS
964     local mntpt_root=$TMP/mntpt/lustre
965     local mntopts=$MNTOPTSTATAHEAD
966
967     echo "Mounting $num_mntpts lustre clients starts on $clients"
968     trap "cleanup_statahead $clients $mntpt_root $num_mntpts" EXIT ERR
969     for i in $(seq 0 $num_mntpts); do
970         zconf_mount_clients $clients ${mntpt_root}$i "$mntopts" ||
971             error_exit "Failed to mount lustre on ${mntpt_root}$i on $clients"
972     done
973
974     do_rpc_nodes $clients cancel_lru_locks mdc
975
976     do_rpc_nodes $clients do_ls $mntpt_root $num_mntpts $dir
977
978     mdsrate_cleanup $((num_clients * 32)) $MACHINEFILE \
979         $num_files $testdir 'f%%d' --ignore
980
981     # use rm instead of rmdir because of
982     # testdir could contain the files created by someone else,
983     # or by previous run where is num_files prev > num_files current
984     rm -rf $testdir
985     cleanup_statahead $clients $mntpt_root $num_mntpts
986 }
987
988 cleanup_rr_alloc () {
989         trap 0
990         local clients="$1"
991         local mntpt_root="$2"
992         local rr_alloc_MNTPTS="$3"
993         local mntpt_dir=$(dirname ${mntpt_root})
994
995         for i in $(seq 0 $((rr_alloc_MNTPTS - 1))); do
996                 zconf_umount_clients $clients ${mntpt_root}$i ||
997                 error_exit "Failed to umount lustre on ${mntpt_root}$i"
998         done
999         do_nodes $clients "rm -rf $mntpt_dir"
1000 }
1001
1002 run_rr_alloc() {
1003         remote_mds_nodsh && skip "remote MDS with nodsh" && return
1004         echo "===Test gives more reproduction percentage if number of "\
1005                 "client and ost are more. Test with 44 or more clients "\
1006                 "and 73 or more OSTs gives 100% reproduction rate=="
1007
1008         RR_ALLOC=${RR_ALLOC:-$(which rr_alloc 2> /dev/null || true)}
1009         [ x$RR_ALLOC = x ] && { skip_env "rr_alloc not found" && return; }
1010         declare -a diff_max_min_arr
1011         # foeo = file on each ost. calc = calculated.
1012         local ost_idx
1013         local foeo_calc
1014         local qos_prec_objs="${TMP}/qos_and_precreated_objects"
1015         local rr_alloc_NFILES=${rr_alloc_NFILES:-555}
1016         local rr_alloc_MNTPTS=${rr_alloc_MNTPTS:-11}
1017         local total_MNTPTS=$((rr_alloc_MNTPTS * num_clients))
1018         local mntpt_root="${TMP}/rr_alloc_mntpt/lustre"
1019         if [ $MDSCOUNT -lt 2 ]; then
1020                 [ -e $DIR/$tdir ] || mkdir -p $DIR/$tdir
1021         else
1022                 [ -e $DIR/$tdir ] || $LFS mkdir -i 0 $DIR/$tdir
1023         fi
1024         chmod 0777 $DIR/$tdir
1025         $SETSTRIPE -c 1 /$DIR/$tdir
1026
1027         trap "cleanup_rr_alloc $clients $mntpt_root $rr_alloc_MNTPTS" EXIT ERR
1028         for i in $(seq 0 $((rr_alloc_MNTPTS - 1))); do
1029                 zconf_mount_clients $clients ${mntpt_root}$i $MOUNT_OPTS ||
1030                 error_exit "Failed to mount lustre on ${mntpt_root}$i $clients"
1031         done
1032
1033         local cmd="$RR_ALLOC $mntpt_root/$tdir/ash $rr_alloc_NFILES \
1034                 $num_clients"
1035
1036         # Save mdt values, set threshold to 100% i.e always Round Robin,
1037         # restore the saved values again after creating files...
1038         save_lustre_params mds1 \
1039                 "lov.$FSNAME-MDT0000*.qos_threshold_rr" > $qos_prec_objs
1040         save_lustre_params mds1 \
1041                 "osp.$FSNAME-OST*-osc-MDT0000.create_count" >> $qos_prec_objs
1042
1043         local old_create_count=$(grep -e "create_count" $qos_prec_objs |
1044                 cut -d'=' -f 2 | sort -nr | head -n1)
1045
1046         # Make sure that every osp has enough precreated objects for the file
1047         # creation app
1048
1049         # create_count is always set to the power of 2 only, so if the files
1050         # per OST are not multiple of that then it will be set to nearest
1051         # lower power of 2. So set 'create_count' to the upper power of 2.
1052
1053         foeo_calc=$((rr_alloc_NFILES * total_MNTPTS / OSTCOUNT))
1054         local create_count=$((2 * foeo_calc))
1055         do_facet mds1 "$LCTL set_param -n \
1056                 lov.$FSNAME-MDT0000*.qos_threshold_rr 100 \
1057                 osp.$FSNAME-OST*-osc-MDT0000.create_count $create_count" ||
1058                 error "failed while setting qos_threshold_rr & creat_count"
1059
1060         # Create few temporary files in order to increase the precreated objects
1061         # to a desired value, before starting 'rr_alloc' app. Due to default
1062         # value 32 of precreation count (OST_MIN_PRECREATE=32), precreated
1063         # objects available are 32 initially, these gets exhausted very soon,
1064         # which causes skip of some osps when very large number of files
1065         # is created per OSTs.
1066         createmany -o $DIR/$tdir/foo- $(((old_create_count + 1) * OSTCOUNT)) \
1067                 > /dev/null
1068         rm -f /$DIR/$tdir/foo*
1069
1070         # Check for enough precreated objects... We should not
1071         # fail here because code(osp_precreate.c) also takes care of it.
1072         # So we have good chances of passing test even if this check fails.
1073         local mdt_idx=0
1074         for ost_idx in $(seq 0 $((OSTCOUNT - 1))); do
1075                 [[ $(precreated_ost_obj_count $mdt_idx $ost_idx) -ge \
1076                         $foeo_calc ]] || echo "Warning: test may fail because" \
1077                         "of lack of precreated objects on OST${ost_idx}"
1078         done
1079
1080         if [[ $total_MNTPTS -ne 0 ]]; then
1081                 # Now start the actual file creation app.
1082                 mpi_run "-np $total_MNTPTS" $cmd || return
1083         else
1084                 error "No mount point"
1085         fi
1086
1087         restore_lustre_params < $qos_prec_objs
1088         rm -f $qos_prec_objs
1089
1090         diff_max_min_arr=($($GETSTRIPE -r $DIR/$tdir/ |
1091                 grep "lmm_stripe_offset:" | awk '{print $2}' | sort -n |
1092                 uniq -c | awk 'NR==1 {min=max=$1} \
1093                 { $1<min ? min=$1 : min; $1>max ? max=$1 : max} \
1094                 END {print max-min, max, min}'))
1095
1096         rm -rf $DIR/$tdir
1097
1098         # In-case of fairly large number of file creation using RR (round-robin)
1099         # there can be two cases in which deviation will occur than the regular
1100         # RR algo behaviour-
1101         # 1- When rr_alloc does not start right with 'lqr_start_count' reseeded,
1102         # 2- When rr_alloc does not finish with 'lqr_start_count == 0'.
1103         # So the difference of files b/w any 2 OST should not be more than 2.
1104         [[ ${diff_max_min_arr[0]} -le 2 ]] ||
1105                 error "Uneven distribution detected: difference between" \
1106                 "maximum files per OST (${diff_max_min_arr[1]}) and" \
1107                 "minimum files per OST (${diff_max_min_arr[2]}) must not be" \
1108                 "greater than 2"
1109 }
1110
1111 run_fs_test() {
1112         # fs_test.x is the default name for exe
1113         FS_TEST=${FS_TEST:=$(which fs_test.x 2> /dev/null || true)}
1114
1115         local clients=${CLIENTS:-$(hostname)}
1116         local testdir=$DIR/d0.fs_test
1117         local file=${testdir}/fs_test
1118         fs_test_threads=${fs_test_threads:-2}
1119         fs_test_type=${fs_test_type:-1}
1120         fs_test_nobj=${fs_test_nobj:-10}
1121         fs_test_check=${fs_test_check:-3}
1122         fs_test_strided=${fs_test_strided:-1}
1123         fs_test_touch=${fs_test_touch:-3}
1124         fs_test_supersize=${fs_test_supersize:-1}
1125         fs_test_op=${fs_test_op:-write}
1126         fs_test_barriers=${fs_test_barriers:-bopen,bwrite,bclose}
1127         fs_test_io=${fs_test_io:-mpi}
1128         fs_test_objsize=${fs_test_objsize:-100}
1129         fs_test_objunit=${fs_test_objunit:-1048576} # 1 mb
1130         fs_test_ndirs=${fs_test_ndirs:-80000}
1131
1132         [ x$FS_TEST = x ] &&
1133                 { skip "FS_TEST not found" && return; }
1134
1135         # Space estimation  in bytes
1136         local space=$(df -B 1 -P $dir | tail -n 1 | awk '{ print $4 }')
1137         local total_threads=$((num_clients * fs_test_threads))
1138         echo "+ $fs_test_objsize * $fs_test_objunit * $total_threads "
1139         if [ $((space / 2)) -le \
1140                 $((fs_test_objsize * fs_test_objunit * total_threads)) ]; then
1141                         fs_test_objsize=$((space / 2 / fs_test_objunit / \
1142                                 total_threads))
1143                         [ $fs_test_objsize -eq 0 ] && \
1144                         skip_env "Need free space more than \
1145                                 $((2 * total_threads * fs_test_objunit)) \
1146                                 : have $((space / fs_test_objunit))" &&
1147                                 return
1148
1149                         echo "(reduced objsize to \
1150                                 $((fs_test_objsize * fs_test_objunit)) bytes)"
1151         fi
1152
1153         print_opts FS_TEST clients fs_test_threads fs_test_objsize MACHINEFILE
1154
1155         mkdir -p $testdir
1156         # mpi_run uses mpiuser
1157         chmod 0777 $testdir
1158
1159         # --nodb          Turn off the database code at runtime
1160         # -g --target     The path to the data file
1161         # -t --type       Whether to do N-N (1) or N-1 (2)
1162         # -n --nobj       The number of objects written/read by each proc
1163         # -z --size       The size of each object
1164         # -d ---num_nn_dirs Number of subdirectories for files
1165         # -C --check      Check every byte using argument 3.
1166         # --collective    Whether to use collective I/O (for N-1, mpi-io only)
1167         # -s --strided    Whether to use a strided pattern (for N-1 only)
1168         # -T --touch      Touch every byte using argument 3
1169         # -o --op         Whether to read only (read) or write only (write)
1170         # -b --barriers   When to barrier.
1171         # -i --io         Use POSIX, MPI, or PLFS IO routines (mpi|posix|plfs)
1172         # -S --supersize  Specify how many objects per superblock
1173
1174         local cmd="$FS_TEST -nodb -g $file -t $fs_test_type -n $fs_test_nobj \
1175                 -z $((fs_test_objsize * fs_test_objunit)) -d $fs_test_ndirs \
1176                 -C $fs_test_check -collective -s $fs_test_strided \
1177                 -T $fs_test_touch -o $fs_test_op -b $fs_test_barriers \
1178                 -i $fs_test_io -S $fs_test_supersize"
1179
1180         echo "+ $cmd"
1181         mpi_run "-np $((num_clients * fs_test_threads))" $cmd
1182
1183         local rc=$?
1184         if [ $rc != 0 ] ; then
1185                 error "fs_test failed! $rc"
1186         fi
1187
1188         rm -rf $testdir
1189 }
1190
1191 ior_mdtest_parallel() {
1192         local rc1=0
1193         local rc2=0
1194         local type=$1
1195
1196         run_ior $type &
1197         local pids=$!
1198
1199         run_mdtest $type || rc2=$?
1200         [[ $rc2 -ne 0 ]] && echo "mdtest failed with error $rc2"
1201
1202         wait $pids || rc1=$?
1203         [[ $rc1 -ne 0 ]] && echo "ior failed with error $rc1"
1204
1205         [[ $rc1 -ne 0 || $rc2 -ne 0 ]] && return 1
1206         return 0
1207 }
1208
1209 run_fio() {
1210         FIO=${FIO:=$(which fio 2> /dev/null || true)}
1211
1212         local clients=${CLIENTS:-$(hostname)}
1213         local fio_jobNum=${fio_jobNum:-4}
1214         local fio_jobFile=${fio_jobFile:-$TMP/fiojobfile.$(date +%s)}
1215         local fio_bs=${fio_bs:-1}
1216         local testdir=$DIR/d0.fio
1217         local file=${testdir}/fio
1218         local runtime=60
1219         local propagate=false
1220
1221         [ "$SLOW" = "no" ] || runtime=600
1222
1223         [ x$FIO = x ] &&
1224                 { skip_env "FIO not found" && return; }
1225
1226         mkdir -p $testdir
1227
1228         # use fio job file if exists,
1229         # create a simple one if missing
1230         if ! [ -f $fio_jobFile ]; then
1231                 cat >> $fio_jobFile <<EOF
1232 [global]
1233 rw=randwrite
1234 size=128m
1235 time_based=1
1236 runtime=$runtime
1237 filename=${file}_\$(hostname)
1238 EOF
1239                 # bs size increased by $i for each job
1240                 for ((i=1; i<=fio_jobNum; i++)); do
1241                         cat >> $fio_jobFile <<EOF
1242
1243 [job$i]
1244 bs=$(( fio_bs * i ))m
1245 EOF
1246                 done
1247                 # job file is created, should be propagated to all clients
1248                 propagate=true
1249         fi
1250
1251
1252         # propagate the job file if not all clients have it yet or
1253         # if the job file was created during the test run
1254         if ! do_nodesv $clients " [ -f $fio_jobFile ] " ||
1255            $propagate; then
1256                 local cfg=$(cat $fio_jobFile)
1257                 do_nodes $clients "echo \\\"$cfg\\\" > ${fio_jobFile}" ||
1258                         error "job file $fio_jobFile is not propagated"
1259                 do_nodesv $clients "cat ${fio_jobFile}"
1260         fi
1261
1262         cmd="$FIO $fio_jobFile"
1263         echo "+ $cmd"
1264
1265         log "clients: $clients $cmd"
1266
1267         local rc=0
1268         do_nodesv $clients "$cmd "
1269         rc=$?
1270
1271         [ $rc = 0 ] || error "fio failed: $rc"
1272         rm -rf $testdir
1273 }
1274
1275 run_xdd() {
1276         XDD=${XDD:=$(which xdd 2> /dev/null || true)}
1277
1278         local clients=${CLIENTS:-$(hostname)}
1279         local testdir=$DIR/d0.xdd
1280         xdd_queuedepth=${xdd_queuedepth:-4}
1281         xdd_blocksize=${xdd_blocksize:-512}
1282         xdd_reqsize=${xdd_reqsize:-128}
1283         xdd_mbytes=${xdd_mbytes:-100}
1284         xdd_passes=${xdd_passes:-40}
1285         xdd_rwratio=${xdd_rwratio:-0}
1286         xdd_ntargets=${xdd_ntargets:-6}
1287         local xdd_custom_params=${xdd_custom_params:-"-dio -stoponerror \
1288                 -maxpri -minall -noproclock -nomemlock"}
1289
1290         [ x$XDD = x ] &&
1291                 { skip "XDD not found" && return; }
1292
1293         print_opts XDD clients xdd_queuedepth xdd_blocksize xdd_reqsize \
1294                 xdd_mbytes xdd_passes xdd_rwratio
1295
1296         mkdir -p $testdir
1297
1298         local files=""
1299         # Target files creates based on the given number of targets
1300         for (( i=0; i < $xdd_ntargets; i++ ))
1301         do
1302                 files+="${testdir}/xdd"$i" "
1303         done
1304
1305         # -targets      specifies the devices or files to perform operation
1306         # -reqsize      number of 'blocks' per operation
1307         # -mbytes       number of 1024*1024-byte blocks to transfer
1308         # -blocksize    size of a single 'block'
1309         # -passes       number of times to read mbytes
1310         # -queuedepth   number of commands to queue on the target
1311         # -rwratio      percentage of read to write operations
1312         # -verbose      will print out statistics on each pass
1313
1314         local cmd="$XDD -targets $xdd_ntargets $files -reqsize $xdd_reqsize \
1315                 -mbytes $xdd_mbytes -blocksize $xdd_blocksize \
1316                 -passes $xdd_passes -queuedepth $xdd_queuedepth \
1317                 -rwratio $xdd_rwratio -verbose $xdd_custom_params"
1318         echo "+ $cmd"
1319
1320         local rc=0
1321         do_nodesv $clients "$cmd "
1322         rc=$?
1323
1324         [ $rc = 0 ] || error "xdd failed: $rc"
1325
1326         rm -rf $testdir
1327 }