#!/usr/bin/perl -w
#
# Copyright 2000-2004 by Hans Reiser, licensing governed by reiserfsprogs/README
#

#
# Mongo.pl is reiserfs benchmark. 
#
# ...
#
# Test will format partition /dev/xxxx by 'mkreiserfs' or 'mke2fs'
# mount it and run given number of processes during each phase :
# Create, Copy, Append, Modify, Holes, Symlinks, Read, Stats, Rename and Delete.
#
# Also, the program calculates the fragmentation after Create and Copy phases:
# Fragm = number_of_fragments / number_of_files 
# (Current version use the files more than 16KB to calculate Fragm.)
#


$EXTENDED_STATISTICS = 0;
$SHOULD_BE_SET = 1;
$ERR_FILE = "ERR.file";

use POSIX;
use File::stat;

# useful info
sub info {

    my @ver;
    $ver[++$#ver] = "date      : " . `date +%c`;
    $ver[++$#ver] = "kernel    : " . `uname -rv`;
    $ver[++$#ver] = "machine   : " . `uname -n`;

    @tmp = `free -t`;
    @foo = split ' ', $tmp [1];
    $ver[++$#ver] = "mem total : " . $foo [1] . "\n";
    $ver[++$#ver] = "reiser4   : " . $OPTIONS{"INFO_R4"} if defined $OPTIONS{"INFO_R4"};
    $ver[++$#ver] = ".config   : PLEASE take care to make .config file available when you publish benchmark results. At namesys, .config should be stored somewhere in the internal benchmark directory (intbenchmarks/mongo/), and link to it is added to the resulting html document.";

    foreach (@ver) {
	chomp $_;
    }
    return @ver;

}

sub print_usage {

    if ($#_ != -1) {
	my ($str) = @_;

	&LOG ("ERROR: $str");
    }

    print "\nUsage: mongo.pl [opt=val | @ ]+ \n"; 

    print "\nRequired options:\n";

    foreach my $k (sort keys %OPTION_DEFS) {
      my $desc = $OPTION_DEFS{$k}->[2];
      print "\t$k\t -- $desc.\n" if ($OPTION_DEFS{$k}->[1]);
    }

    print "\nOther options:\n";

    foreach my $k (sort keys %OPTION_DEFS) {
      my $desc = $OPTION_DEFS{$k}->[2];
      print "\t$k\t -- $desc.\n" unless ($OPTION_DEFS{$k}->[1]);
    }

    print "\nSpecial options and commands are:\n";
    print "\tLOG=\t -- open a log file for recording test progress.\n";
    print "\tRUN\t -- start test.\n";
    print "\t\@\t -- include a file with options and commands.\n";

    exit -1;
}

# description of all valid options which can be in an input file is a
# hash array each element of it has the following form: 

# "key" => [default value, flags, comment string]

#
# currently flags field can be:
#
#   0 --- parameter is optional
#
#   1 --- parameter is mandatory
#

%OPTION_DEFS = (
	    "FSTYPE"  => [ undef, $SHOULD_BE_SET, "filesystem type"],
	    "DIR"     => [ undef, $SHOULD_BE_SET, "fs mount point"],
	    "MKFS"    => [ undef, 0, "mkfs command"],
	    "DEV"     => [ undef, $SHOULD_BE_SET, "device file name"],
	    "JOURNAL_DEV"     => [ undef, 0, "journal device file name"],
	    "JOURNAL_SIZE"     => [ undef, 0, "journal size in 4K blocks"],
	    "MOUNT_OPTIONS"  => [ undef, 0, "mount options"],
	    "BYTES"   => [ undef, $SHOULD_BE_SET,
			   "file set size (in bytes) created by all instances of reiser_fract_tree in one pass, "],
	    "FILE_SIZE" => [ undef, $SHOULD_BE_SET,
			     "median file size (a reiser_fract_tree parameter)"],
	    "REP_COUNTER" => [3, 1, "number of passes for each mongo phase"],
	    "NPROC" => [1, 1, "number of concurrent processes"],
	    "WRITE_BUFFER" => [4096, 1,
			       "read/write buffer size used for read and write by mongo utilities"],
	    "GAMMA" => ["0.0", 1,
			       "the exponent of the core distribution in reiser_fract_tree"],
	    "MAX_FNAME_LEN" => [24, 0, "maximum generated file name length default (24)"],
	    "SYNC" => ["off", 1, "using O_SYNC when opening files"],
	    "DD_MBCOUNT" => [undef, 0, "size of largefile in megabytes"],
	    "INFO_R4" => [undef, 0, "reiser4 version specification"],
            "PHASE_MKDIRS" => [undef, 0, "setting for mkdirs phase: off/on"],
            "PHASE_MKFILES" => [undef, 0, "setting for mkfiles phase: off/on"],
            "PHASE_CREATE" => [undef, 0, "setting for create phase: off/on"],
            "PHASE_COPY" => [undef, 0, "setting for copy phase: off/cp/list"],
            "PHASE_APPEND" => [undef, 0, "setting for append phase: off/on"],
            "PHASE_MODIFY" => [undef, 0, "setting for modify phase: off/on"],
            "PHASE_OVERWRITE" => [undef, 0, "setting for overwrite phase: off/on"],
            "PHASE_READ" => [undef, 0, "setting for read phase: off/on"],
            "PHASE_STATS" => [undef, 0, "setting for stats phase: off/on"],
            "PHASE_DELETE" => [undef, 0, "setting for delete phase: off/rm/list"]
);

# current set of (option, value) pairs.
%OPTIONS=();

#--------------------------------------------
# Set working directories
#--------------------------------------------
chomp($TOPDIR  = `pwd`);
# print "topdir is *$TOPDIR* \n";

# $READIT      = "${TOPDIR}/mongo_read";
# $SLINKS      = "${TOPDIR}/mongo_slinks";
    
# Keep the largest file to one fifth (100 million bytes) 
# of the total tree size.
#-------------------------------------------------------
$max_file_size = 100000000;

# Yuri Shevchuk says that 0 is the median size 
# in real life, so I believe him.
#----------------------------------------------
$median_dir_nr_files = 0;

# This should be larger, change once done testing.
#-------------------------------------------------
$max_file_size    = 1000000;

$median_dir_nr_files    = 1000;
$max_directory_nr_files = 100000; 

$median_dir_branching = 3;
$max_dir_branching    = 3; 

# This should be varying, someday....
#------------------------------------
$append_factor = 0.5;

# ... compile utilities
#---------------------
&compile;

#------- Subroutines --------------------------------------
#----------------------------------------------------------

#
# return number of 1k block used on filesystem $mp
#
sub get_blocks_usage ($) {
  my ($mp) = @_;
  my $df = `df -k $mp | tail -n 1`;
  chomp $df;
  my @items = split / +/, $df;
  return $items[2];
}

# split a cmd "opt=val" into an array of opt and val elements.
sub split_cmd ($)
{
    my ($str) = @_;
    my ($beg,@rest) = split /=/, $str;

    return  ($beg, join ("=", @rest));
}

sub mount_fsys
{    
    my ($mopts, $fstype, $dev, $dir);


    $mopts  = "";
    $mopts  = "-o ". $OPTIONS{"MOUNT_OPTIONS"} if defined $OPTIONS{"MOUNT_OPTIONS"};

    $fstype = $OPTIONS{"FSTYPE"};
    $dev    = $OPTIONS{"DEV"};
    $dir    = $OPTIONS{"DIR"};

    # LOG dmd
    &LOG(  "mount $mopts -t $fstype $dev $dir");
    system("mount $mopts -t $fstype $dev $dir");
    &DIE ("can\'t mount fs") if ($?);
}

sub umount_fsys
{
    my ($die_on_error) = @_;
    my $dir;

    $dir = $OPTIONS{"DIR"};
    # added sync dmd
    system("sync; umount $dir;");
    &DIE ("can\'t umount") if ($die_on_error && $?);
}

# Prepare a fs for tests: mkfs and mount
sub init_fsys
{
    my ($mkfs, $dir, $fstype);

    &umount_fsys(0);

    $dir = $OPTIONS{"DIR"};
    # replaced by call to umount_fsys above     
    #system ("umount $dir") ;
    $fstype = $OPTIONS{"FSTYPE"};


    unless (defined ($OPTIONS{"MKFS"})) {
	# we know what mkfs should be for some fs types (reiserfs, ext2)
	if ( $fstype eq "reiserfs") {
	  $mkfs = "echo y | mkreiserfs";
	  if (defined ($OPTIONS{"JOURNAL_DEV"})){
	    $mkfs = $mkfs . " -j " . $OPTIONS{"JOURNAL_DEV"};
	  }
	  if (defined ($OPTIONS{"JOURNAL_SIZE"})) {
	    $mkfs = $mkfs . " -s " . $OPTIONS{"JOURNAL_SIZE"};
	  }
	} elsif ($fstype eq "ext2") {
	    $mkfs = "mke2fs";
	} elsif ($fstype eq "reiser4") {
	    $mkfs = "mkfs.reiser4 -q ";
	} else {
	    $mkfs = "mkfs.$fstype";
	}
    } else {
	$mkfs = $OPTIONS{"MKFS"};
    }

    $mkfs = "$mkfs " . $OPTIONS{"DEV"};

    &LOG("$mkfs");
    system ($mkfs);
    &DIE ("can\'t create fs") if ($?);

    &mount_fsys;

    # disk usage statistics gets initialized here
    $used0 = get_blocks_usage($dir);

#   dmd only do this at the end (see done_fsys)
#   &umount_fsys;

}

# Cleanup a fs
sub done_fsys
{
   # dmd added sync to umount_fsys
   # system("sync"); 
   &umount_fsys(1);
}

# wait all spawned jobs to complete and DIE if one returns not a success.
sub wait_all
{
    my $pid = 0;
    while (($pid = wait) != -1) {
	&DIE ("child process returned error $?") if ($?);
    }
}

# a logger
sub LOG ($)
{
    my ($msg) = @_;

    print ($LOGFILE $msg . "\n") if ($LOGFILE);
    print $msg . "\n";
}

# abort with proper log message.
sub DIE ($)
{
    my ($e) = @_;

    print "ERROR $e \n";
    exit (-1);
}

# compile c-files if it is necessary
sub compile 
{
    system("make all > .mongo.compile.log 2>&1");
    &DIE("compile failed") if ($?);
}

sub proc_stat
{
    my $t = `cat /proc/stat | grep "^cpu "`;
    my @t =  split /\s+/,$t;

    return  splice( @t, 1, 5);
}

# reset statistics before test
sub stats_before
{
    my ($test_name) = @_;

    @proc_stat0 = &proc_stat;
    # ...
}

# gather  /  /  statistics after the
# test completes

sub stats_after ($)
{
    my ($test_name) = @_;

    @proc_stat1 = &proc_stat;
    my ($time_user, $time_nice, $time_system, $time_idle, $time_iowait) = 
	($proc_stat1[0] - $proc_stat0[0], $proc_stat1[1] - $proc_stat0[1],
	 $proc_stat1[2] - $proc_stat0[2], $proc_stat1[3] - $proc_stat0[3],
	 $proc_stat1[4] - $proc_stat0[4]);

    my $cpu_utilization = $time_system / 
	($time_user + $time_nice + $time_system + $time_idle + $time_iowait);
    LOG (sprintf ("STAT CPU_UTIL %.2f", $cpu_utilization * 100));

    my $dir = $OPTIONS{"DIR"};

    $used = get_blocks_usage($dir) - $used0;

    LOG "STAT DF ${used}";

    if ($EXTENDED_STATISTICS) {
	    
	open (FIND_PIPE, "find $dir |") || die "cannnot open pipe from \"find\": $!\n";

	my $dirs = 0;
	my $files = 0;
	my $files16 = 0;

	while() {
	    chomp;
	    $st = lstat ($_);
	    if (S_ISDIR($st->mode)) {
		$dirs ++;
	    } elsif (S_ISREG($st->mode)) {
		$files ++;
		$files16 ++ if ($st->size > 16384);
	    }
	}

	close (FIND_PIPE);

	&LOG ("NDIRS $dirs");
	&LOG ("NFILES $files");
	    
	#$f=$frag;
	my $f16  = $files16;
	my $fr16 =`find ${dir} -type f -size +16k | xargs $TOPDIR/map5 | $TOPDIR/summ | tail -n 1 2>&1`;
	my @ff16= split ' ', $f16;  
	my @ffr16= split ' ', $fr16;
	$files16 = $ff16[0];  
	my $frag = $ffr16[0];  
	if ( $files16 != 0) {
	    $procent = $frag / $files16;
	} else {
	    $procent = 0;
	}

	&LOG ("FRAGM " . sprintf ("%.2f", $procent));
    }
}

#------------------------------------------------------------------
# Mongo Launcher mongo_launcher(test_name, test_cmd, do_single_test)
#------------------------------------------------------------------
sub mongo_launcher ()
{
    # command string in $test_cmd may containg substrings %rep% and %proc%
    # tokens which will be replaced by the ordinal number of this test phase
    # run and ordinal number of the working process.

    my ($test_name, $test_cmd, $rep_count, $proc_count) = @_;
    my ($i, $j);

    $rep_count = $OPTIONS{"REP_COUNTER"};
    $proc_count = $OPTIONS{"NPROC"};

    LOG "PHASE ${test_name}";

    #   dmd this is now only done once in init_fsys
    #    &mount_fsys;

    &stats_before;

    my $total_real = 0;
    my $total_cpu = 0;
    
    for (my $i = 0; $i < $rep_count; $i++) {

	my $cmd = $test_cmd;

	$cmd =~ s/%rep%/$i/g;

    #$com is  $OPTIONS{"NPROC"} + 1 commands (plus wait) per repetition concatenated by '&' 
 	my $com;
	
	for ($j = 0; $j < $proc_count; $j ++) {

	     # A local variable $cmd is set from outer $cmd. It is
	     # strange, but this Perl trick works!

	    my $cmd = $cmd;

	    $cmd =~ s/%proc%/$j/g;
	    
	    $com .= "( $cmd ) & ";
	  }

        $com .= " wait; sync";    

LOG "CMNT ${test_name} command: ${com}";

	my @time_output=`(/usr/bin/time -p /bin/sh -c \" $com \" ) 2>&1`; 
	if ( -e ${ERR_FILE}) {
	    &DIE ("\nEXITED WITH FAIL\n");
	}
	my $skip = 0;

	# skip two lines of output (n+m records in, n+m recourds out )
	# for each process in case of dd test
	# FIXME(Zam): redirection of dd output should help to get rid of this.
 
	$skip = 2 * $proc_count if ($test_name eq "dd_writing_largefile" || 
				    $test_name eq "dd_reading_largefile");

	my $real = (split ' ', $time_output[$skip])[1];
	my $cpu  = (split ' ', $time_output[2+$skip])[1];

	unless ( $real =~ /\s*\d+/ && $cpu =~ /\s*\d+/) {
	  LOG "@time_output"; 
	}

	$total_real += $real;
        $total_cpu  += $cpu;
      }

    LOG "STAT REAL_TIME $total_real";
    LOG "STAT CPU_TIME $total_cpu"; 

    &stats_after($test_name);

    #  dmd: only do this at very end    
    #  &umount_fsys;

#    print "\nPress  to continue ... ";
#    my $tmp = ;
}


sub mongo_phase ()
{
    my ($name, $handler, $rep_count, $proc_count) = @_;
    my $opt = get_param("PHASE_" . $name);

    if ($opt ne "off") {
        return &$handler($name, $opt, $rep_count, $proc_count);
    }
}

$check_err = " || touch " . $ERR_FILE;


sub mkdir_phase()
{
    my ($name, $opt, $rep_count, $proc_count) = @_;

    my $cmd =
        "cd " . $OPTIONS{"DIR"} . "; $TOPDIR/mongo_mkdirs <  $TOPDIR/dirs.txt; cd $TOPDIR";
    &mongo_launcher ($name, $cmd);
}

sub mkfile_phase()
{
    my ($name, $opt, $rep_count, $proc_count) = @_;

    my $cmd =
        "cd " . $OPTIONS{"DIR"} . "; $TOPDIR/mongo_mkfiles <  $TOPDIR/files.txt; cd $TOPDIR";
    &mongo_launcher ($name, $cmd);
}


#
# Handler for CREATE phase of mongo.
#
# Calls reiser_fract_tree with given parameters.
#
sub create_phase ()
{
    my ($name, $opt, $rep_count, $proc_count) = @_;

    my $cmd = 
	"$TOPDIR/reiser_fract_tree $bytes_for_one_process " . 
	$OPTIONS{"FILE_SIZE"} . 
	" $max_file_size $median_dir_nr_files " .
	"$max_directory_nr_files $median_dir_branching $max_dir_branching ".
	$OPTIONS{"WRITE_BUFFER"} . " " .
	"$DIR0 $print_stats_flag " . $OPTIONS{"MAX_FNAME_LEN"} . " $FLIST0 " .
	$OPTIONS{"SYNC"} . " " . $OPTIONS{"GAMMA"} . " $check_err";
    &mongo_launcher ($name, $cmd);
}

#
# Handler for COPY phase of mongo.
#
# In "cp" mode, invokes cp(1) to copy files.
#
# In "list" mode (deafult) uses mongo_copy to copy files and update file lists.
#
sub copy_phase ()
{
    my ($name, $opt) = @_;

    my $write_buffer = $OPTIONS{"WRITE_BUFFER"};
    my $sync = $OPTIONS{"SYNC"};

    if ($opt eq "cp") {
        &mongo_launcher ($name, "$TOPDIR/update-flist.pl $DIR0 $DIR1 $FLIST0 $FLIST1 " .
	                 $check_err);
    } elsif (($opt eq "") or ($opt eq "list")) {
        &mongo_launcher ($name,
			 "cat $FLIST0 | " .
			 "$TOPDIR/mongo_copy $DIR0 $DIR1 $write_buffer $FLIST1 $sync $check_err");
    } else {
       &DIE("Unknown copy phase option `$opt'\n");
    }
}

#
# Handler for APPEND phase of mongo.
#
# Calls mongo_append with given parameters.
#
sub append_phase ()
{
    my ($name, $opt) = @_;
    my $prefix;

    my $write_buffer = $OPTIONS{"WRITE_BUFFER"};
    my $sync = $OPTIONS{"SYNC"};

    if ($opt eq "find") {
        $prefix="find $DIR01 -type f";
    } elsif (($opt eq "") or ($opt eq "on") or ($opt eq "list")) {
        $prefix="cat $FLIST01 | $DIR_FILTER";
    }
    &mongo_launcher ($name,
		     "$prefix | $TOPDIR/mongo_append $append_factor $write_buffer $sync $check_err");
}

#
# Handler for MODIFY phase of mongo.
#
# Calls mongo_modify with given parameters.
#
sub modify_phase ()
{
    my ($name, $opt) = @_;
    my $prefix;

    my $write_buffer = $OPTIONS{"WRITE_BUFFER"};
    my $sync = $OPTIONS{"SYNC"};

    if ($opt eq "find") {
        $prefix="find $DIR01 -type f";
    } elsif (($opt eq "") or ($opt eq "on") or ($opt eq "list")) {
        $prefix="cat $FLIST01 | $DIR_FILTER";
    }

    &mongo_launcher ($name,
		     "$prefix | $TOPDIR/mongo_modify 0.02 $write_buffer $sync" .
		     $check_err);
}

#
# Handler for OVERWRITE phase of mongo.
#
# Calls mongo_modify with given parameters.
#
sub overwrite_phase ()
{
    my ($name, $opt) = @_;
    my $prefix;

    my $write_buffer = $OPTIONS{"WRITE_BUFFER"};
    my $sync = $OPTIONS{"SYNC"};

    if ($opt eq "find") {
        $prefix="find $DIR01 -type f";
    } elsif (($opt eq "") or ($opt eq "on") or ($opt eq "list")) {
        $prefix="cat $FLIST01 | $DIR_FILTER";
    }

    &mongo_launcher ($name,
		     "$prefix | $TOPDIR/mongo_modify 1 $write_buffer $sync" .
		     $check_err);
}

#
# Handler for SLINKS phase of mongo.
#
# Calls mongo_slinks.
#
sub slinks_phase ()
{
    my ($name, $opt) = @_;
    my $prefix;

    if ($opt eq "find") {
        $prefix="find $DIR01";
    } elsif (($opt eq "") or ($opt eq "on") or ($opt eq "list")) {
        $prefix="cat $FLIST01";
    }

    &mongo_launcher ($name,
		     "$prefix | while read X; do echo \\\$X \\\$X.lnk ; done | $TOPDIR/mongo_slinks");
}


#
# Handler for READ phase of mongo.
#
# Calls mongo_read.
#
sub read_phase ()
{
    my ($name, $opt) = @_;
    my $prefix;

    if ($opt eq "find") {
        $prefix="find $DIR01 -type f";
    } elsif (($opt eq "") or ($opt eq "on") or ($opt eq "list")) {
        $prefix="cat $FLIST01 | $DIR_FILTER";
    }

    &mongo_launcher ($name,
        "$prefix | $TOPDIR/mongo_read" . $check_err);
}

#
# Handler for STATS phase of mongo.
#
# Performs stat(2) of each file in the working set, and, also readdir(2) on each
# directory.
#
sub stats_phase ()
{
    my ($name, $opt) = @_;
    my $prefix;

    if ($opt eq "find") {
        $prefix="find $DIR01 -name not_found ; true";
    } elsif (($opt eq "") or ($opt eq "on") or ($opt eq "list")) {
        $prefix="(cat $FLIST01 | xargs ls -d) > /dev/null";
    }

    &mongo_launcher ($name, $prefix . $check_err);
}

#
# Handler for RENAME phase of mongo.
#
# Moves files from source directory $DIR0 to target $DIR1.
#
sub rename_phase ()
{
    my ($name, $opt) = @_;
    my $prefix;

    if ($opt eq "find") {
        $prefix="find $DIR01 -type f";
    } elsif (($opt eq "") or ($opt eq "on") or ($opt eq "list")) {
        $prefix="cat $FLIST01 | $DIR_FILTER";
    }

    &mongo_launcher ($name,
		     "$prefix | " .
		     "perl -e \'while (<>) { chomp; \\\$n=\\\$o=\\\$_; \\\$n=~s|$DIR0|$DIR1| ; rename (\\\$o, \\\$n.r); }\'");
}


#
# Handler for DELETE phase of mongo.
#
# In "cp" mode, uses rm(1) to delete working set.
#
# In "list" mode (deafult) uses mongo_delete to delete working set.
#
sub delete_phase ()
{
    my ($name, $opt) = @_;

    if ($opt eq "rm") {
        &mongo_launcher ($name, "rm -r $DIR01" . $check_err);
    } elsif (($opt eq "") or ($opt eq "list")) {
        &mongo_launcher ($name,
		     "cat $FLIST01 | $TOPDIR/mongo_delete ". $OPTIONS{"DIR"} .
		     " $check_err");
    } else {
       &DIE("Unknown delete phase option `$opt'\n");
    }
}

#
# Handler for DD WRITE phase of mongo.
#
sub dd_write_phase ()
{
    my ($name, $opt) = @_;

    my $dir = $OPTIONS{"DIR"};
    my $mbcount = $OPTIONS{"DD_MBCOUNT"};

    &mongo_launcher($name, "dd if=/dev/zero of=$dir/largefile  bs=1M count=$mbcount");
}

#
# Handler for DD READ phase of mongo.
#
sub dd_read_phase ()
{
    my ($name, $opt) = @_;

    my $dir = $OPTIONS{"DIR"};
    my $mbcount = $OPTIONS{"DD_MBCOUNT"};

    &mongo_launcher($name, "dd of=/dev/null if=$dir/largefile  bs=1M count=$mbcount");
}

#------------------------------------------------------------------
# The main MONGO routine with does several tests sequentially.
#------------------------------------------------------------------
sub mongo
{
    &init_fsys;       # make and mount the file system

#    my $check_err = " || ( err=\$\?; echo FAILED \$err > " . $ERR_FILE;

    `rm ${ERR_FILE}` if (-e ${ERR_FILE});
    system("rm -f /var/tmp/mongo.flist*");


    &mongo_phase("MKDIRS", \&mkdir_phase);
    &mongo_phase("MKFILES", \&mkfile_phase);

    &mongo_phase("CREATE", \&create_phase);
    &mongo_phase("COPY", \©_phase);
    &mongo_phase("APPEND", \&append_phase);
    &mongo_phase("MODIFY", \&modify_phase);
    &mongo_phase("OVERWRITE", \&overwrite_phase);
#   &mongo_phase("SLINKS", \&slinks_phase);
    &mongo_phase("READ", \&read_phase);
    &mongo_phase("STATS", \&stats_phase);
#   &mongo_phase("RENAME", \&rename_phase);
    &mongo_phase("DELETE", \&delete_phase);

    if ($OPTIONS{"DD_MBCOUNT"}) {
      &LOG ("SER DD_MBCOUNT=" . $OPTIONS{"DD_MBCOUNT"});
      &mongo_phase ("dd_writing_largefile", \&dd_write_phase);
      &mongo_phase ("dd_reading_largefile", \&dd_read_phase);
    }

    system("rm -f /var/tmp/mongo.flist*");

    &done_fsys;
}

# parse a